├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── proto └── simpledb.proto └── src ├── api.rs ├── api ├── connection.rs ├── driver.rs ├── embedded.rs ├── embedded │ ├── embeddedconnection.rs │ ├── embeddeddriver.rs │ ├── embeddedmetadata.rs │ ├── embeddedresultset.rs │ └── embeddedstatement.rs ├── metadata.rs ├── network.rs ├── network │ ├── networkconnection.rs │ ├── networkdriver.rs │ ├── networkmetadata.rs │ ├── networkresultset.rs │ ├── networkstatement.rs │ ├── remoteconnection.rs │ ├── remotemetadata.rs │ ├── remoteresultset.rs │ └── remotestatement.rs ├── resultset.rs └── statement.rs ├── buffer.rs ├── buffer ├── buffer.rs ├── bufferfiletest.rs ├── buffermgr.rs ├── buffermgrtest.rs └── buffertest.rs ├── file.rs ├── file ├── blockid.rs ├── filemgr.rs ├── filetest.rs └── page.rs ├── index.rs ├── index ├── btree.rs ├── btree │ ├── btpage.rs │ ├── btreedir.rs │ ├── btreeindex.rs │ ├── btreeleaf.rs │ └── direntry.rs ├── hash.rs ├── hash │ └── hashindex.rs ├── index.rs ├── indexretrievaltest.rs ├── indexupdatetest.rs ├── planner.rs ├── planner │ ├── indexjoinplan.rs │ ├── indexselectplan.rs │ └── indexupdateplanner.rs ├── query.rs └── query │ ├── indexjoinscan.rs │ ├── indexjointest.rs │ ├── indexselectscan.rs │ └── indexselecttest.rs ├── log.rs ├── log ├── logiterator.rs ├── logmgr.rs └── logtest.rs ├── main.rs ├── materialize.rs ├── materialize ├── aggregationfn.rs ├── countfn.rs ├── groupbyplan.rs ├── groupbyscan.rs ├── groupbytest.rs ├── groupvalue.rs ├── materializeplan.rs ├── maxfn.rs ├── mergejoinplan.rs ├── mergejoinscan.rs ├── mergejointest.rs ├── recordcomparator.rs ├── sortplan.rs ├── sortscan.rs ├── sorttest.rs └── temptable.rs ├── metadata.rs ├── metadata ├── catalogtest.rs ├── indexinfo.rs ├── indexmgr.rs ├── metadatamgr.rs ├── metadatamgrtest.rs ├── statinfo.rs ├── statmgr.rs ├── tablemgr.rs ├── tablemgrtest.rs └── viewmgr.rs ├── multibuffer.rs ├── multibuffer ├── bufferneeds.rs ├── chunkscan.rs ├── multibufferproductplan.rs └── multibufferproductscan.rs ├── opt.rs ├── opt ├── heuristicqueryplanner.rs └── tableplanner.rs ├── parse.rs ├── parse ├── badsyntaxerror.rs ├── createindexdata.rs ├── createtabledata.rs ├── createviewdata.rs ├── deletedata.rs ├── insertdata.rs ├── lexer.rs ├── lexertest.rs ├── modifydata.rs ├── parser.rs ├── parsertest.rs └── querydata.rs ├── plan.rs ├── plan ├── basicqueryplanner.rs ├── basicupdateplanner.rs ├── betterqueryplanner.rs ├── multitableplantest.rs ├── optimizedproductplan.rs ├── plan.rs ├── planner.rs ├── plannerstudenttest.rs ├── plannertest1.rs ├── plannertest2.rs ├── productplan.rs ├── projectplan.rs ├── queryplanner.rs ├── selectplan.rs ├── singletableplantest.rs ├── tableplan.rs └── updateplanner.rs ├── query.rs ├── query ├── constant.rs ├── expression.rs ├── predicate.rs ├── productscan.rs ├── projectscan.rs ├── projecttest.rs ├── scan.rs ├── scantest1.rs ├── scantest2.rs ├── selectscan.rs ├── term.rs └── updatescan.rs ├── record.rs ├── record ├── layout.rs ├── layouttest.rs ├── recordpage.rs ├── recordtest.rs ├── rid.rs ├── schema.rs ├── tablescan.rs └── tablescantest.rs ├── server.rs ├── server └── simpledb.rs ├── startserver.rs ├── tx.rs └── tx ├── bufferlist.rs ├── concurrency.rs ├── concurrency ├── concurrencymgr.rs └── locktable.rs ├── concurrencytest.rs ├── recovery.rs ├── recovery ├── checkpointrecord.rs ├── commitrecord.rs ├── logrecord.rs ├── recoverymgr.rs ├── recoverytest.rs ├── rollbackrecord.rs ├── setintrecord.rs ├── setstringrecord.rs └── startrecord.rs ├── transaction.rs └── txtest.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Install Protoc 19 | uses: arduino/setup-protoc@v1 20 | - uses: actions/checkout@v3 21 | - name: Build 22 | run: cargo build --verbose 23 | - name: Run tests 24 | run: cargo test --verbose 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /buffermgrtest/ 2 | /buffertest/ 3 | /filetest/ 4 | /logtest/ 5 | /studentdb/ 6 | /target -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simpledb-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | default-run = "main" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [[bin]] 10 | name = "main" 11 | path = "src/main.rs" 12 | 13 | [[bin]] 14 | name = "server" 15 | path = "src/startserver.rs" 16 | 17 | [dependencies] 18 | once_cell = "1.12.0" 19 | rand = "0.8.5" 20 | tonic = "0.8.0" 21 | tokio = { version = "1.20.1", features = ["rt-multi-thread"] } 22 | prost = "0.11.0" 23 | enum_dispatch = "0.3.8" 24 | 25 | [build-dependencies] 26 | tonic-build = { version = "0.8.0", features = ["prost"] } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simpledb-rs 2 | 3 | [SimpleDB](http://cs.bc.edu/~sciore/simpledb/) re-written in Rust. 4 | 5 | ## Requirements 6 | 7 | * [`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html) 8 | * [`protoc`](https://grpc.io/docs/protoc-installation/) >= 3 9 | 10 | ## How to run 11 | 12 | ``` 13 | $ git clone https://github.com/mnogu/simpledb-rs.git 14 | $ cd simpledb-rs 15 | ``` 16 | 17 | ### Embedded 18 | 19 | ``` 20 | $ cargo run 21 | Finished dev [unoptimized + debuginfo] target(s) in 0.21s 22 | Running `target/debug/main` 23 | Connect> foo 24 | creating new database 25 | transaction 1 committed 26 | 27 | SQL> create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int) 28 | transaction 2 committed 29 | 0 records processed 30 | 31 | SQL> insert into STUDENT(SId, SName, MajorId, GradYear) values (1, 'joe', 10, 2021) 32 | transaction 3 committed 33 | 1 records processed 34 | 35 | SQL> select SId, SName, MajorId, GradYear from student 36 | sid sname majorid gradyear 37 | ----------------------------------- 38 | 1 joe 10 2021 39 | transaction 4 committed 40 | ``` 41 | 42 | ### Network 43 | 44 | ``` 45 | $ cargo run --bin server 46 | Finished dev [unoptimized + debuginfo] target(s) in 0.14s 47 | Running `target/debug/server` 48 | creating new database 49 | transaction 1 committed 50 | ``` 51 | 52 | ``` 53 | $ cargo run 54 | Finished dev [unoptimized + debuginfo] target(s) in 0.13s 55 | Running `target/debug/main` 56 | Connect> //[::1] 57 | 58 | SQL> create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int) 59 | 0 records processed 60 | 61 | SQL> insert into STUDENT(SId, SName, MajorId, GradYear) values (1, 'joe', 10, 2021) 62 | 1 records processed 63 | 64 | SQL> select SId, SName, MajorId, GradYear from student 65 | sid sname majorid gradyear 66 | ----------------------------------- 67 | 1 joe 10 2021 68 | ``` 69 | 70 | ## Status 71 | 72 | - [x] Disk and File Management 73 | - [x] Memory Management 74 | - [x] Transaction Management 75 | - [x] Record Management 76 | - [x] Metadata Management 77 | - [x] Query Processing 78 | - [x] Parsing 79 | - [x] Planning 80 | - [ ] JDBC Interfaces 81 | - [x] Uses [gRPC](https://grpc.io/) and [Protocol Buffers](https://developers.google.com/protocol-buffers) instead 82 | - [x] Indexing 83 | - [x] Materialization and Sorting 84 | - [x] Effective Buffer Utilization 85 | - [x] Query Optimization 86 | 87 | ## References 88 | 89 | * [Database Design and Implementation](https://link.springer.com/book/10.1007/978-3-030-33836-7) 90 | * [The SimpleDB Database System](http://cs.bc.edu/~sciore/simpledb/) -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | fn main() { 4 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 5 | tonic_build::configure() 6 | .file_descriptor_set_path(out_dir.join("simpledb_descriptor.bin")) 7 | .compile(&["proto/simpledb.proto"], &["proto"]) 8 | .unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /proto/simpledb.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package simpledb; 4 | 5 | service Connection { 6 | rpc Close (CloseConnectionRequest) returns (CloseConnectionReply) {} 7 | rpc Commit (CommitRequest) returns (CommitReply) {} 8 | rpc Rollback (RollbackRequest) returns (RollbackReply) {} 9 | } 10 | 11 | message CloseConnectionRequest {} 12 | message CloseConnectionReply {} 13 | 14 | message CommitRequest {} 15 | message CommitReply {} 16 | 17 | message RollbackRequest {} 18 | message RollbackReply {} 19 | 20 | 21 | service MetaData { 22 | rpc GetColumnCount (GetColumnCountRequest) returns (GetColumnCountReply) {} 23 | rpc GetColumnName (GetColumnNameRequest) returns (GetColumnNameReply) {} 24 | rpc GetColumnType (GetColumnTypeRequest) returns (GetColumnTypeReply) {} 25 | rpc GetColumnDisplaySize (GetColumnDisplaySizeRequest) returns (GetColumnDisplaySizeReply) {} 26 | } 27 | 28 | message GetColumnCountRequest { 29 | uint64 id = 1; 30 | } 31 | message GetColumnCountReply { 32 | uint64 count = 1; 33 | } 34 | 35 | message GetColumnNameRequest { 36 | uint64 id = 1; 37 | uint64 index = 2; 38 | } 39 | message GetColumnNameReply { 40 | string name = 1; 41 | } 42 | 43 | message GetColumnTypeRequest { 44 | uint64 id = 1; 45 | uint64 index = 2; 46 | } 47 | enum Type { 48 | TYPE_UNSPECIFIED = 0; 49 | TYPE_INTEGER = 4; 50 | TYPE_VARCHAR = 12; 51 | 52 | } 53 | message GetColumnTypeReply { 54 | Type type = 1; 55 | } 56 | 57 | message GetColumnDisplaySizeRequest { 58 | uint64 id = 1; 59 | uint64 index = 2; 60 | } 61 | message GetColumnDisplaySizeReply { 62 | uint64 size = 1; 63 | } 64 | 65 | service ResultSet { 66 | rpc Next (NextRequest) returns (NextReply) {} 67 | rpc GetInt (GetIntRequest) returns (GetIntReply) {} 68 | rpc GetString (GetStringRequest) returns (GetStringReply) {} 69 | rpc Close(CloseResultSetRequest) returns (CloseResultSetReply) {} 70 | } 71 | 72 | message NextRequest { 73 | uint64 id = 1; 74 | } 75 | message NextReply { 76 | bool has_next = 1; 77 | } 78 | 79 | message GetIntRequest { 80 | uint64 id = 1; 81 | string name = 2; 82 | } 83 | message GetIntReply { 84 | int32 value = 1; 85 | } 86 | 87 | message GetStringRequest { 88 | uint64 id = 1; 89 | string name = 2; 90 | } 91 | message GetStringReply { 92 | string value = 1; 93 | } 94 | 95 | message CloseResultSetRequest { 96 | uint64 id = 1; 97 | } 98 | message CloseResultSetReply { 99 | } 100 | 101 | service Statement { 102 | rpc ExecuteQuery (ExecuteQueryRequest) returns (ExecuteQueryReply) {} 103 | rpc ExecuteUpdate (ExecuteUpdateRequest) returns (ExecuteUpdateReply) {} 104 | } 105 | 106 | message ExecuteQueryRequest { 107 | string query = 1; 108 | } 109 | message ExecuteQueryReply { 110 | uint64 id = 1; 111 | } 112 | 113 | message ExecuteUpdateRequest { 114 | string command = 1; 115 | } 116 | message ExecuteUpdateReply { 117 | uint64 count = 1; 118 | } -------------------------------------------------------------------------------- /src/api.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | pub mod driver; 3 | pub mod embedded; 4 | pub mod metadata; 5 | pub mod network; 6 | pub mod resultset; 7 | pub mod statement; 8 | -------------------------------------------------------------------------------- /src/api/connection.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use super::{ 4 | driver::SQLError, 5 | embedded::{embeddedconnection::EmbeddedConnection, embeddedstatement::EmbeddedStatement}, 6 | network::{networkconnection::NetworkConnection, networkstatement::NetworkStatement}, 7 | statement::Statement, 8 | }; 9 | 10 | pub trait ConnectionControl { 11 | fn close(&mut self) -> Result<(), SQLError>; 12 | fn commit(&mut self) -> Result<(), SQLError>; 13 | fn rollback(&mut self) -> Result<(), SQLError>; 14 | } 15 | 16 | pub enum Connection { 17 | Embedded(Arc>), 18 | Network(Arc>), 19 | } 20 | 21 | impl From>> for Connection { 22 | fn from(c: Arc>) -> Self { 23 | Connection::Embedded(c) 24 | } 25 | } 26 | 27 | impl From>> for Connection { 28 | fn from(c: Arc>) -> Self { 29 | Connection::Network(c) 30 | } 31 | } 32 | 33 | impl ConnectionControl for Connection { 34 | fn close(&mut self) -> Result<(), SQLError> { 35 | match self { 36 | Connection::Embedded(conn) => conn.lock().unwrap().close(), 37 | Connection::Network(conn) => conn.lock().unwrap().close(), 38 | } 39 | } 40 | 41 | fn commit(&mut self) -> Result<(), SQLError> { 42 | match self { 43 | Connection::Embedded(conn) => conn.lock().unwrap().commit(), 44 | Connection::Network(conn) => conn.lock().unwrap().commit(), 45 | } 46 | } 47 | 48 | fn rollback(&mut self) -> Result<(), SQLError> { 49 | match self { 50 | Connection::Embedded(conn) => conn.lock().unwrap().rollback(), 51 | Connection::Network(conn) => conn.lock().unwrap().rollback(), 52 | } 53 | } 54 | } 55 | 56 | impl Connection { 57 | #[allow(dead_code)] 58 | pub fn create_statement(&self) -> Statement { 59 | match self { 60 | Connection::Embedded(conn) => Statement::Embedded(EmbeddedStatement::new(conn.clone())), 61 | Connection::Network(conn) => Statement::Network(NetworkStatement::new(conn.clone())), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/api/driver.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | use tonic::Status; 3 | 4 | use crate::{ 5 | buffer::buffermgr::AbortError, plan::plan::PlanError, tx::transaction::TransactionError, 6 | }; 7 | 8 | use super::{ 9 | connection::Connection, embedded::embeddeddriver::EmbeddedDriver, 10 | network::networkdriver::NetworkDriver, 11 | }; 12 | 13 | #[derive(Debug)] 14 | pub enum SQLError { 15 | Abort(AbortError), 16 | IO(std::io::Error), 17 | Plan(PlanError), 18 | Status(Status), 19 | Transaction(TransactionError), 20 | Transport(tonic::transport::Error), 21 | General, 22 | } 23 | 24 | impl From for SQLError { 25 | fn from(e: AbortError) -> Self { 26 | SQLError::Abort(e) 27 | } 28 | } 29 | 30 | impl From for SQLError { 31 | fn from(e: std::io::Error) -> Self { 32 | SQLError::IO(e) 33 | } 34 | } 35 | 36 | impl From for SQLError { 37 | fn from(e: PlanError) -> Self { 38 | SQLError::Plan(e) 39 | } 40 | } 41 | 42 | impl From for SQLError { 43 | fn from(e: Status) -> Self { 44 | SQLError::Status(e) 45 | } 46 | } 47 | 48 | impl From for SQLError { 49 | fn from(e: TransactionError) -> Self { 50 | SQLError::Transaction(e) 51 | } 52 | } 53 | 54 | impl From for SQLError { 55 | fn from(e: tonic::transport::Error) -> Self { 56 | SQLError::Transport(e) 57 | } 58 | } 59 | 60 | #[enum_dispatch(Driver)] 61 | pub trait DriverControl { 62 | fn connect(&self, url: &str) -> Result; 63 | } 64 | 65 | #[enum_dispatch] 66 | pub enum Driver { 67 | Embedded(EmbeddedDriver), 68 | Network(NetworkDriver), 69 | } 70 | -------------------------------------------------------------------------------- /src/api/embedded.rs: -------------------------------------------------------------------------------- 1 | pub mod embeddedconnection; 2 | pub mod embeddeddriver; 3 | pub mod embeddedmetadata; 4 | pub mod embeddedresultset; 5 | pub mod embeddedstatement; 6 | -------------------------------------------------------------------------------- /src/api/embedded/embeddedconnection.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | api::{connection::ConnectionControl, driver::SQLError}, 5 | plan::planner::Planner, 6 | server::simpledb::SimpleDB, 7 | tx::transaction::Transaction, 8 | }; 9 | 10 | pub struct EmbeddedConnection { 11 | db: SimpleDB, 12 | current_tx: Arc>, 13 | planner: Option>>, 14 | } 15 | 16 | impl EmbeddedConnection { 17 | pub fn new(db: SimpleDB) -> Result { 18 | let current_tx = Arc::new(Mutex::new(db.new_tx()?)); 19 | let planner = db.planner(); 20 | Ok(EmbeddedConnection { 21 | db, 22 | current_tx, 23 | planner, 24 | }) 25 | } 26 | 27 | pub(in crate::api) fn get_transaction(&self) -> Arc> { 28 | self.current_tx.clone() 29 | } 30 | 31 | pub(in crate::api) fn planner(&self) -> Option>> { 32 | self.planner.clone() 33 | } 34 | } 35 | 36 | impl ConnectionControl for EmbeddedConnection { 37 | fn close(&mut self) -> Result<(), SQLError> { 38 | Ok(self.current_tx.lock().unwrap().commit()?) 39 | } 40 | 41 | fn commit(&mut self) -> Result<(), SQLError> { 42 | self.current_tx.lock().unwrap().commit()?; 43 | self.current_tx = Arc::new(Mutex::new(self.db.new_tx()?)); 44 | Ok(()) 45 | } 46 | 47 | fn rollback(&mut self) -> Result<(), SQLError> { 48 | self.current_tx.lock().unwrap().rollback()?; 49 | self.current_tx = Arc::new(Mutex::new(self.db.new_tx()?)); 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/api/embedded/embeddeddriver.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | api::{ 5 | connection::Connection, 6 | driver::{DriverControl, SQLError}, 7 | }, 8 | server::simpledb::SimpleDB, 9 | }; 10 | 11 | use super::embeddedconnection::EmbeddedConnection; 12 | 13 | pub struct EmbeddedDriver {} 14 | 15 | impl EmbeddedDriver { 16 | #[allow(dead_code)] 17 | pub fn new() -> EmbeddedDriver { 18 | EmbeddedDriver {} 19 | } 20 | } 21 | 22 | impl DriverControl for EmbeddedDriver { 23 | fn connect(&self, url: &str) -> Result { 24 | let mut dbname = url; 25 | if let Some(idx) = dbname.rfind(':') { 26 | dbname = &dbname[idx + 1..] 27 | } 28 | let db = SimpleDB::new(dbname)?; 29 | Ok(Arc::new(Mutex::new(EmbeddedConnection::new(db)?)).into()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/embedded/embeddedmetadata.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp, sync::Arc}; 2 | 3 | use crate::{ 4 | api::{driver::SQLError, metadata::MetaDataControl}, 5 | record::schema::{Schema, Type}, 6 | }; 7 | 8 | pub struct EmbeddedMetaData { 9 | sch: Arc, 10 | } 11 | 12 | impl EmbeddedMetaData { 13 | pub fn new(sch: Arc) -> EmbeddedMetaData { 14 | EmbeddedMetaData { sch } 15 | } 16 | } 17 | 18 | impl MetaDataControl for EmbeddedMetaData { 19 | fn get_column_count(&mut self) -> Result { 20 | Ok(self.sch.fields().len()) 21 | } 22 | 23 | fn get_column_name(&mut self, column: usize) -> Result { 24 | if let Some(name) = self.sch.fields().get(column - 1) { 25 | return Ok(name.to_string()); 26 | } 27 | Err(SQLError::General) 28 | } 29 | 30 | fn get_column_type(&mut self, column: usize) -> Result { 31 | let fldname = self.get_column_name(column)?; 32 | Ok(self.sch.type_(&fldname)) 33 | } 34 | 35 | fn get_column_display_size(&mut self, column: usize) -> Result { 36 | let fldname = self.get_column_name(column)?; 37 | let fldtype = self.sch.type_(&fldname); 38 | let fldlength = match fldtype { 39 | Type::Integer => 6, 40 | Type::Varchar => self.sch.length(&fldname), 41 | }; 42 | Ok(cmp::max(fldname.len(), fldlength) + 1) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/api/embedded/embeddedresultset.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | api::{ 5 | connection::ConnectionControl, driver::SQLError, metadata::MetaData, 6 | resultset::ResultSetControl, 7 | }, 8 | plan::plan::{Plan, PlanControl}, 9 | query::scan::{Scan, ScanControl}, 10 | record::schema::Schema, 11 | tx::transaction::TransactionError, 12 | }; 13 | 14 | use super::{embeddedconnection::EmbeddedConnection, embeddedmetadata::EmbeddedMetaData}; 15 | 16 | pub struct EmbeddedResultSet { 17 | s: Scan, 18 | sch: Arc, 19 | conn: Arc>, 20 | } 21 | 22 | impl EmbeddedResultSet { 23 | pub fn new( 24 | plan: Plan, 25 | conn: Arc>, 26 | ) -> Result { 27 | Ok(EmbeddedResultSet { 28 | s: plan.open()?, 29 | sch: plan.schema(), 30 | conn, 31 | }) 32 | } 33 | } 34 | 35 | impl ResultSetControl for EmbeddedResultSet { 36 | fn next(&mut self) -> Result { 37 | if let Ok(r) = self.s.next() { 38 | return Ok(r); 39 | } 40 | self.conn.lock().unwrap().rollback()?; 41 | Err(SQLError::General) 42 | } 43 | 44 | fn get_int(&mut self, fldname: &str) -> Result { 45 | let fldname = fldname.to_lowercase(); 46 | if let Ok(v) = self.s.get_int(&fldname) { 47 | return Ok(v); 48 | } 49 | self.conn.lock().unwrap().rollback()?; 50 | Err(SQLError::General) 51 | } 52 | 53 | fn get_string(&mut self, fldname: &str) -> Result { 54 | let fldname = fldname.to_lowercase(); 55 | if let Ok(v) = self.s.get_string(&fldname) { 56 | return Ok(v); 57 | } 58 | self.conn.lock().unwrap().rollback()?; 59 | Err(SQLError::General) 60 | } 61 | 62 | fn get_meta_data(&self) -> MetaData { 63 | EmbeddedMetaData::new(self.sch.clone()).into() 64 | } 65 | 66 | fn close(&mut self) -> Result<(), SQLError> { 67 | self.s.close()?; 68 | self.conn.lock().unwrap().close() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/api/embedded/embeddedstatement.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::api::{ 4 | connection::ConnectionControl, driver::SQLError, resultset::ResultSet, 5 | statement::StatementControl, 6 | }; 7 | 8 | use super::{embeddedconnection::EmbeddedConnection, embeddedresultset::EmbeddedResultSet}; 9 | 10 | pub struct EmbeddedStatement { 11 | conn: Arc>, 12 | } 13 | 14 | impl EmbeddedStatement { 15 | pub fn new(conn: Arc>) -> EmbeddedStatement { 16 | EmbeddedStatement { conn } 17 | } 18 | } 19 | 20 | impl StatementControl for EmbeddedStatement { 21 | fn execute_query(&mut self, qry: &str) -> Result { 22 | let tx = self.conn.lock().unwrap().get_transaction(); 23 | let planner = self.conn.lock().unwrap().planner(); 24 | if let Some(planner) = planner { 25 | let pln = planner.lock().unwrap().create_query_plan(qry, tx); 26 | if let Ok(pln) = pln { 27 | if let Ok(s) = EmbeddedResultSet::new(pln, self.conn.clone()) { 28 | return Ok(s.into()); 29 | } 30 | } 31 | } 32 | self.conn.lock().unwrap().rollback()?; 33 | Err(SQLError::General) 34 | } 35 | 36 | fn execute_update(&mut self, cmd: &str) -> Result { 37 | let tx = self.conn.lock().unwrap().get_transaction(); 38 | let planner = self.conn.lock().unwrap().planner(); 39 | if let Some(planner) = planner { 40 | let result = planner.lock().unwrap().execute_update(cmd, tx); 41 | if let Ok(result) = result { 42 | if self.conn.lock().unwrap().commit().is_ok() { 43 | return Ok(result); 44 | } 45 | } 46 | } 47 | self.conn.lock().unwrap().rollback()?; 48 | Err(SQLError::General) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/api/metadata.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use crate::record::schema::Type; 4 | 5 | use super::{ 6 | driver::SQLError, embedded::embeddedmetadata::EmbeddedMetaData, 7 | network::networkmetadata::NetworkMetaData, 8 | }; 9 | 10 | #[enum_dispatch(MetaData)] 11 | pub trait MetaDataControl { 12 | fn get_column_count(&mut self) -> Result; 13 | fn get_column_name(&mut self, column: usize) -> Result; 14 | fn get_column_type(&mut self, column: usize) -> Result; 15 | fn get_column_display_size(&mut self, column: usize) -> Result; 16 | } 17 | 18 | #[enum_dispatch] 19 | pub enum MetaData { 20 | Embedded(EmbeddedMetaData), 21 | Network(NetworkMetaData), 22 | } 23 | -------------------------------------------------------------------------------- /src/api/network.rs: -------------------------------------------------------------------------------- 1 | pub mod networkconnection; 2 | pub mod networkdriver; 3 | pub mod networkmetadata; 4 | pub mod networkresultset; 5 | pub mod networkstatement; 6 | pub mod simpledb { 7 | tonic::include_proto!("simpledb"); 8 | } 9 | pub mod remoteconnection; 10 | pub mod remotemetadata; 11 | pub mod remoteresultset; 12 | pub mod remotestatement; 13 | -------------------------------------------------------------------------------- /src/api/network/networkconnection.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | 3 | use tokio::runtime::Runtime; 4 | use tonic::transport::{Channel, Endpoint}; 5 | 6 | use crate::api::{connection::ConnectionControl, driver::SQLError}; 7 | 8 | use super::simpledb::{ 9 | connection_client::ConnectionClient, CloseConnectionRequest, CommitRequest, 10 | RollbackRequest, 11 | }; 12 | 13 | pub struct NetworkConnection { 14 | channel: Channel, 15 | rt: Runtime, 16 | client: ConnectionClient, 17 | } 18 | 19 | impl NetworkConnection { 20 | pub fn new(endpoint: Endpoint) -> Result { 21 | let rt = tokio::runtime::Builder::new_current_thread() 22 | .enable_all() 23 | .build()?; 24 | let channel = rt.block_on(endpoint.connect())?; 25 | let client = ConnectionClient::new(channel.clone()); 26 | Ok(NetworkConnection { 27 | channel, 28 | rt, 29 | client, 30 | }) 31 | } 32 | 33 | pub(in crate::api::network) fn channel(&self) -> Channel { 34 | self.channel.clone() 35 | } 36 | 37 | pub(in crate::api::network) fn run(&self, future: F) -> F::Output 38 | where 39 | F: Future, 40 | { 41 | self.rt.block_on(future) 42 | } 43 | } 44 | 45 | impl ConnectionControl for NetworkConnection { 46 | fn close(&mut self) -> Result<(), SQLError> { 47 | let request = tonic::Request::new(CloseConnectionRequest {}); 48 | self.rt.block_on(self.client.close(request))?; 49 | Ok(()) 50 | } 51 | 52 | fn commit(&mut self) -> Result<(), SQLError> { 53 | let request = tonic::Request::new(CommitRequest {}); 54 | self.rt.block_on(self.client.commit(request))?; 55 | Ok(()) 56 | } 57 | 58 | fn rollback(&mut self) -> Result<(), SQLError> { 59 | let request = tonic::Request::new(RollbackRequest {}); 60 | self.rt.block_on(self.client.rollback(request))?; 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/api/network/networkdriver.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tonic::transport::Endpoint; 4 | 5 | use crate::api::{ 6 | connection::Connection, 7 | driver::{DriverControl, SQLError}, 8 | }; 9 | 10 | use super::networkconnection::NetworkConnection; 11 | 12 | pub struct NetworkDriver {} 13 | 14 | impl NetworkDriver { 15 | #[allow(dead_code)] 16 | pub fn new() -> NetworkDriver { 17 | NetworkDriver {} 18 | } 19 | } 20 | 21 | impl DriverControl for NetworkDriver { 22 | fn connect(&self, url: &str) -> Result { 23 | let mut host = url; 24 | if let Some(idx) = host.find("//") { 25 | host = &host[idx + 2..]; 26 | } 27 | let endpoint = Endpoint::from_shared(format!("http://{}:1099", host))?; 28 | Ok(Arc::new(Mutex::new(NetworkConnection::new(endpoint)?)).into()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/network/networkmetadata.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tonic::transport::Channel; 4 | 5 | use crate::{ 6 | api::{driver::SQLError, metadata::MetaDataControl}, 7 | record::schema::Type, 8 | }; 9 | 10 | use super::{ 11 | networkconnection::NetworkConnection, 12 | simpledb::{ 13 | meta_data_client::MetaDataClient, GetColumnCountRequest, GetColumnDisplaySizeRequest, 14 | GetColumnNameRequest, GetColumnTypeRequest, 15 | }, 16 | }; 17 | 18 | pub struct NetworkMetaData { 19 | conn: Arc>, 20 | client: MetaDataClient, 21 | id: u64, 22 | } 23 | 24 | impl NetworkMetaData { 25 | pub fn new(conn: Arc>, id: u64) -> NetworkMetaData { 26 | let client = MetaDataClient::new(conn.lock().unwrap().channel()); 27 | NetworkMetaData { conn, client, id } 28 | } 29 | } 30 | 31 | impl MetaDataControl for NetworkMetaData { 32 | fn get_column_count(&mut self) -> Result { 33 | let request = tonic::Request::new(GetColumnCountRequest { id: self.id }); 34 | let response = self 35 | .conn 36 | .lock() 37 | .unwrap() 38 | .run(self.client.get_column_count(request))?; 39 | Ok(response.into_inner().count as usize) 40 | } 41 | 42 | fn get_column_name(&mut self, column: usize) -> Result { 43 | let request = tonic::Request::new(GetColumnNameRequest { 44 | id: self.id, 45 | index: column as u64, 46 | }); 47 | let response = self 48 | .conn 49 | .lock() 50 | .unwrap() 51 | .run(self.client.get_column_name(request))?; 52 | Ok(response.into_inner().name) 53 | } 54 | 55 | fn get_column_type(&mut self, column: usize) -> Result { 56 | let request = tonic::Request::new(GetColumnTypeRequest { 57 | id: self.id, 58 | index: column as u64, 59 | }); 60 | let response = self 61 | .conn 62 | .lock() 63 | .unwrap() 64 | .run(self.client.get_column_type(request))?; 65 | match response.into_inner().r#type { 66 | 4 => Ok(Type::Integer), 67 | 12 => Ok(Type::Varchar), 68 | _ => Err(SQLError::General), 69 | } 70 | } 71 | 72 | fn get_column_display_size(&mut self, column: usize) -> Result { 73 | let request = tonic::Request::new(GetColumnDisplaySizeRequest { 74 | id: self.id, 75 | index: column as u64, 76 | }); 77 | let response = self 78 | .conn 79 | .lock() 80 | .unwrap() 81 | .run(self.client.get_column_display_size(request))?; 82 | Ok(response.into_inner().size as usize) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/api/network/networkresultset.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tonic::transport::Channel; 4 | 5 | use crate::api::{driver::SQLError, metadata::MetaData, resultset::ResultSetControl}; 6 | 7 | use super::{ 8 | networkconnection::NetworkConnection, 9 | networkmetadata::NetworkMetaData, 10 | simpledb::{ 11 | result_set_client::ResultSetClient, CloseResultSetRequest, GetIntRequest, GetStringRequest, 12 | NextRequest, 13 | }, 14 | }; 15 | 16 | pub struct NetworkResultSet { 17 | conn: Arc>, 18 | client: ResultSetClient, 19 | id: u64, 20 | } 21 | 22 | impl NetworkResultSet { 23 | pub fn new(conn: Arc>, id: u64) -> NetworkResultSet { 24 | let client = ResultSetClient::new(conn.lock().unwrap().channel()); 25 | NetworkResultSet { conn, client, id } 26 | } 27 | } 28 | 29 | impl ResultSetControl for NetworkResultSet { 30 | fn next(&mut self) -> Result { 31 | let request = tonic::Request::new(NextRequest { id: self.id }); 32 | let response = self.conn.lock().unwrap().run(self.client.next(request))?; 33 | Ok(response.into_inner().has_next) 34 | } 35 | 36 | fn get_int(&mut self, fldname: &str) -> Result { 37 | let request = tonic::Request::new(GetIntRequest { 38 | id: self.id, 39 | name: fldname.to_string(), 40 | }); 41 | let response = self 42 | .conn 43 | .lock() 44 | .unwrap() 45 | .run(self.client.get_int(request))?; 46 | Ok(response.into_inner().value) 47 | } 48 | 49 | fn get_string(&mut self, fldname: &str) -> Result { 50 | let request = tonic::Request::new(GetStringRequest { 51 | id: self.id, 52 | name: fldname.to_string(), 53 | }); 54 | let response = self 55 | .conn 56 | .lock() 57 | .unwrap() 58 | .run(self.client.get_string(request))?; 59 | Ok(response.into_inner().value) 60 | } 61 | 62 | fn get_meta_data(&self) -> MetaData { 63 | NetworkMetaData::new(self.conn.clone(), self.id).into() 64 | } 65 | 66 | fn close(&mut self) -> Result<(), SQLError> { 67 | let request = tonic::Request::new(CloseResultSetRequest { id: self.id }); 68 | self.conn.lock().unwrap().run(self.client.close(request))?; 69 | Ok(()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/api/network/networkstatement.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tonic::transport::Channel; 4 | 5 | use crate::api::{driver::SQLError, resultset::ResultSet, statement::StatementControl}; 6 | 7 | use super::{ 8 | networkconnection::NetworkConnection, 9 | networkresultset::NetworkResultSet, 10 | simpledb::{statement_client::StatementClient, ExecuteQueryRequest, ExecuteUpdateRequest}, 11 | }; 12 | 13 | pub struct NetworkStatement { 14 | conn: Arc>, 15 | client: StatementClient, 16 | } 17 | 18 | impl NetworkStatement { 19 | #[allow(dead_code)] 20 | pub fn new(conn: Arc>) -> NetworkStatement { 21 | let client = StatementClient::new(conn.lock().unwrap().channel()); 22 | NetworkStatement { conn, client } 23 | } 24 | } 25 | 26 | impl StatementControl for NetworkStatement { 27 | fn execute_query(&mut self, qry: &str) -> Result { 28 | let request = tonic::Request::new(ExecuteQueryRequest { 29 | query: qry.to_string(), 30 | }); 31 | let response = self 32 | .conn 33 | .lock() 34 | .unwrap() 35 | .run(self.client.execute_query(request))?; 36 | Ok(NetworkResultSet::new(self.conn.clone(), response.into_inner().id).into()) 37 | } 38 | 39 | fn execute_update(&mut self, cmd: &str) -> Result { 40 | let request = tonic::Request::new(ExecuteUpdateRequest { 41 | command: cmd.to_string(), 42 | }); 43 | let response = self 44 | .conn 45 | .lock() 46 | .unwrap() 47 | .run(self.client.execute_update(request))?; 48 | Ok(response.into_inner().count as usize) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/api/network/remoteconnection.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tonic::{Request, Response, Status}; 4 | 5 | use crate::api::connection::ConnectionControl; 6 | use crate::api::driver::SQLError; 7 | use crate::api::embedded::embeddedconnection::EmbeddedConnection; 8 | use crate::server::simpledb::SimpleDB; 9 | 10 | use super::remotestatement::RemoteStatement; 11 | use super::simpledb::connection_server::Connection; 12 | use super::simpledb::{ 13 | CloseConnectionReply, CloseConnectionRequest, CommitReply, CommitRequest, RollbackReply, 14 | RollbackRequest, 15 | }; 16 | 17 | pub struct RemoteConnection { 18 | conn: Arc>, 19 | } 20 | 21 | impl RemoteConnection { 22 | #[allow(dead_code)] 23 | pub fn new(db: SimpleDB) -> Result { 24 | let conn = Arc::new(Mutex::new(EmbeddedConnection::new(db)?)); 25 | Ok(RemoteConnection { conn }) 26 | } 27 | 28 | #[allow(dead_code)] 29 | pub fn create_statement(&self) -> RemoteStatement { 30 | RemoteStatement::new(self.conn.clone()) 31 | } 32 | } 33 | 34 | #[tonic::async_trait] 35 | impl Connection for RemoteConnection { 36 | async fn close( 37 | &self, 38 | _: Request, 39 | ) -> Result, Status> { 40 | if self.conn.lock().unwrap().close().is_err() { 41 | return Err(Status::internal("failed to close")); 42 | } 43 | let reply = CloseConnectionReply {}; 44 | Ok(Response::new(reply)) 45 | } 46 | 47 | async fn commit(&self, _: Request) -> Result, Status> { 48 | if self.conn.lock().unwrap().commit().is_err() { 49 | return Err(Status::internal("failed to commit")); 50 | } 51 | let reply = CommitReply {}; 52 | Ok(Response::new(reply)) 53 | } 54 | 55 | async fn rollback( 56 | &self, 57 | _: Request, 58 | ) -> Result, Status> { 59 | if self.conn.lock().unwrap().rollback().is_err() { 60 | return Err(Status::internal("failed to rollback")); 61 | } 62 | let reply = RollbackReply {}; 63 | Ok(Response::new(reply)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/api/network/remoteresultset.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use tonic::{Request, Response, Status}; 7 | 8 | use crate::api::{embedded::embeddedresultset::EmbeddedResultSet, resultset::ResultSetControl}; 9 | 10 | use super::simpledb::{ 11 | result_set_server::ResultSet, CloseResultSetReply, CloseResultSetRequest, GetIntReply, 12 | GetIntRequest, GetStringReply, GetStringRequest, NextReply, NextRequest, 13 | }; 14 | 15 | pub struct RemoteResultSet { 16 | rss: Arc>>, 17 | } 18 | 19 | impl RemoteResultSet { 20 | #[allow(dead_code)] 21 | pub fn new(rss: Arc>>) -> RemoteResultSet { 22 | RemoteResultSet { rss } 23 | } 24 | } 25 | 26 | #[tonic::async_trait] 27 | impl ResultSet for RemoteResultSet { 28 | async fn next(&self, request: Request) -> Result, Status> { 29 | let request = request.into_inner(); 30 | let mut rss = self.rss.lock().unwrap(); 31 | let rs = rss.get_mut(&request.id); 32 | if let Some(rs) = rs { 33 | if let Ok(has_next) = rs.next() { 34 | let reply = NextReply { has_next }; 35 | return Ok(Response::new(reply)); 36 | } 37 | } 38 | Err(Status::internal("failed to be next")) 39 | } 40 | 41 | async fn get_int( 42 | &self, 43 | request: Request, 44 | ) -> Result, Status> { 45 | let request = request.into_inner(); 46 | let mut rss = self.rss.lock().unwrap(); 47 | let rs = rss.get_mut(&request.id); 48 | if let Some(rs) = rs { 49 | if let Ok(value) = rs.get_int(&request.name) { 50 | let reply = GetIntReply { value }; 51 | return Ok(Response::new(reply)); 52 | } 53 | } 54 | Err(Status::internal("failed to get the integer")) 55 | } 56 | 57 | async fn get_string( 58 | &self, 59 | request: Request, 60 | ) -> Result, Status> { 61 | let request = request.into_inner(); 62 | let mut rss = self.rss.lock().unwrap(); 63 | let rs = rss.get_mut(&request.id); 64 | if let Some(rs) = rs { 65 | if let Ok(value) = rs.get_string(&request.name) { 66 | let reply = GetStringReply { value }; 67 | return Ok(Response::new(reply)); 68 | } 69 | } 70 | Err(Status::internal("failed to get the string")) 71 | } 72 | 73 | async fn close( 74 | &self, 75 | request: Request, 76 | ) -> Result, Status> { 77 | let request = request.into_inner(); 78 | let mut rss = self.rss.lock().unwrap(); 79 | let rs = rss.get_mut(&request.id); 80 | if let Some(rs) = rs { 81 | if rs.close().is_ok() { 82 | rss.remove(&request.id); 83 | let reply = CloseResultSetReply {}; 84 | return Ok(Response::new(reply)); 85 | } 86 | } 87 | Err(Status::internal("failed to close")) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/api/network/remotestatement.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use tonic::{Request, Response, Status}; 7 | 8 | use crate::api::{ 9 | embedded::{ 10 | embeddedconnection::EmbeddedConnection, embeddedresultset::EmbeddedResultSet, 11 | embeddedstatement::EmbeddedStatement, 12 | }, 13 | resultset::ResultSet, 14 | statement::StatementControl, 15 | }; 16 | 17 | use super::simpledb::{ 18 | statement_server::Statement, ExecuteQueryReply, ExecuteQueryRequest, ExecuteUpdateReply, 19 | ExecuteUpdateRequest, 20 | }; 21 | 22 | pub struct RemoteStatement { 23 | stmt: Arc>, 24 | rss: Arc>>, 25 | } 26 | 27 | impl RemoteStatement { 28 | pub fn new(conn: Arc>) -> RemoteStatement { 29 | let stmt = Arc::new(Mutex::new(EmbeddedStatement::new(conn))); 30 | let rss = Arc::new(Mutex::new(HashMap::new())); 31 | RemoteStatement { stmt, rss } 32 | } 33 | 34 | #[allow(dead_code)] 35 | pub fn result_sets(&self) -> Arc>> { 36 | self.rss.clone() 37 | } 38 | } 39 | 40 | #[tonic::async_trait] 41 | impl Statement for RemoteStatement { 42 | async fn execute_query( 43 | &self, 44 | request: Request, 45 | ) -> Result, Status> { 46 | let rs = self 47 | .stmt 48 | .lock() 49 | .unwrap() 50 | .execute_query(&request.into_inner().query); 51 | if let Ok(ResultSet::Embedded(rs)) = rs { 52 | let id = self.rss.lock().unwrap().len() as u64; 53 | self.rss.lock().unwrap().insert(id, rs); 54 | let reply = ExecuteQueryReply { id }; 55 | return Ok(Response::new(reply)); 56 | } 57 | Err(Status::internal("failed to execute the query")) 58 | } 59 | 60 | async fn execute_update( 61 | &self, 62 | request: Request, 63 | ) -> Result, Status> { 64 | let count = self 65 | .stmt 66 | .lock() 67 | .unwrap() 68 | .execute_update(&request.into_inner().command); 69 | if let Ok(count) = count { 70 | let reply = ExecuteUpdateReply { 71 | count: count as u64, 72 | }; 73 | return Ok(Response::new(reply)); 74 | } 75 | Err(Status::internal("failed to execute the update")) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/api/resultset.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use super::{ 4 | driver::SQLError, embedded::embeddedresultset::EmbeddedResultSet, metadata::MetaData, 5 | network::networkresultset::NetworkResultSet, 6 | }; 7 | 8 | #[enum_dispatch(ResultSet)] 9 | pub trait ResultSetControl { 10 | fn next(&mut self) -> Result; 11 | fn get_int(&mut self, fldname: &str) -> Result; 12 | fn get_string(&mut self, fldname: &str) -> Result; 13 | fn get_meta_data(&self) -> MetaData; 14 | fn close(&mut self) -> Result<(), SQLError>; 15 | } 16 | 17 | #[enum_dispatch] 18 | pub enum ResultSet { 19 | Embedded(EmbeddedResultSet), 20 | Network(NetworkResultSet), 21 | } 22 | -------------------------------------------------------------------------------- /src/api/statement.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use super::{ 4 | driver::SQLError, embedded::embeddedstatement::EmbeddedStatement, 5 | network::networkstatement::NetworkStatement, resultset::ResultSet, 6 | }; 7 | 8 | #[enum_dispatch(Statement)] 9 | pub trait StatementControl { 10 | fn execute_query(&mut self, qry: &str) -> Result; 11 | fn execute_update(&mut self, cmd: &str) -> Result; 12 | } 13 | 14 | #[enum_dispatch] 15 | pub enum Statement { 16 | Embedded(EmbeddedStatement), 17 | Network(NetworkStatement), 18 | } 19 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | pub mod buffer; 2 | pub mod bufferfiletest; 3 | pub mod buffermgr; 4 | pub mod buffermgrtest; 5 | pub mod buffertest; 6 | -------------------------------------------------------------------------------- /src/buffer/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | file::{blockid::BlockId, filemgr::FileMgr, page::Page}, 8 | log::logmgr::LogMgr, 9 | }; 10 | 11 | pub struct Buffer { 12 | fm: Arc, 13 | lm: Arc>, 14 | contents: Page, 15 | blk: Option, 16 | pins: i32, 17 | txnum: Option, 18 | lsn: Option, 19 | } 20 | 21 | impl Buffer { 22 | pub fn new(fm: Arc, lm: Arc>) -> Buffer { 23 | let blocksize = fm.block_size(); 24 | Buffer { 25 | fm, 26 | lm, 27 | contents: Page::new(blocksize), 28 | blk: None, 29 | pins: 0, 30 | txnum: None, 31 | lsn: None, 32 | } 33 | } 34 | 35 | pub fn contents(&mut self) -> &mut Page { 36 | &mut self.contents 37 | } 38 | 39 | pub fn block(&self) -> &Option { 40 | &self.blk 41 | } 42 | 43 | pub fn set_modified(&mut self, txnum: usize, lsn: Option) { 44 | self.txnum = Some(txnum); 45 | if lsn.is_some() { 46 | self.lsn = lsn; 47 | } 48 | } 49 | 50 | pub fn is_pinned(&self) -> bool { 51 | self.pins > 0 52 | } 53 | 54 | pub fn modifying_tx(&self) -> Option { 55 | self.txnum 56 | } 57 | 58 | pub(in crate::buffer) fn assign_to_block(&mut self, b: BlockId) -> Result<(), Error> { 59 | self.flush()?; 60 | self.blk = Some(b.clone()); 61 | self.fm.read(&b, &mut self.contents)?; 62 | self.pins = 0; 63 | Ok(()) 64 | } 65 | 66 | pub(in crate::buffer) fn flush(&mut self) -> Result<(), Error> { 67 | if self.txnum.is_some() { 68 | if let Some(lsn) = self.lsn { 69 | self.lm.lock().unwrap().flush(lsn)?; 70 | } 71 | if let Some(ref blk) = self.blk { 72 | self.fm.write(blk, &mut self.contents)?; 73 | } 74 | self.txnum = None; 75 | } 76 | Ok(()) 77 | } 78 | 79 | pub(in crate::buffer) fn pin(&mut self) { 80 | self.pins += 1; 81 | } 82 | 83 | pub(in crate::buffer) fn unpin(&mut self) { 84 | self.pins -= 1; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/buffer/bufferfiletest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::fs; 4 | 5 | use crate::{ 6 | file::{blockid::BlockId, page::Page}, 7 | server::simpledb::SimpleDB, 8 | }; 9 | 10 | #[test] 11 | fn bufferfiletest() { 12 | let mut db = SimpleDB::with_params("bufferfiletest", 400, 8).unwrap(); 13 | let m = db.buffer_mgr(); 14 | let mut bm = m.lock().unwrap(); 15 | let blk = BlockId::new("testfile", 2); 16 | let pos1 = 88; 17 | 18 | let idx1 = bm.pin(&blk).unwrap(); 19 | let b1 = bm.buffer(idx1); 20 | let p1 = b1.contents(); 21 | p1.set_string(pos1, "abcdefghijklm"); 22 | let size = Page::max_length("abcdefghijklm".len()); 23 | let pos2 = pos1 + size; 24 | p1.set_int(pos2, 345); 25 | b1.set_modified(1, Some(0)); 26 | bm.unpin(idx1); 27 | 28 | let idx2 = bm.pin(&blk).unwrap(); 29 | let b2 = bm.buffer(idx2); 30 | let p2 = b2.contents(); 31 | assert_eq!(pos2, 105); 32 | assert_eq!(p2.get_int(pos2), 345); 33 | assert_eq!(pos1, 88); 34 | assert_eq!(p2.get_string(pos1).unwrap(), "abcdefghijklm"); 35 | bm.unpin(idx2); 36 | 37 | fs::remove_dir_all("bufferfiletest").unwrap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/buffer/buffermgrtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{collections::HashMap, fs}; 4 | 5 | use crate::{file::blockid::BlockId, server::simpledb::SimpleDB}; 6 | 7 | #[test] 8 | fn buffermgrtest() { 9 | let mut db = SimpleDB::with_params("buffermgrtest", 400, 3).unwrap(); 10 | let m = db.buffer_mgr(); 11 | let mut bm = m.lock().unwrap(); 12 | bm.set_max_time(1); 13 | 14 | let mut buff = Vec::with_capacity(6); 15 | buff.push(bm.pin(&BlockId::new("testfile", 0)).unwrap()); 16 | buff.push(bm.pin(&BlockId::new("testfile", 1)).unwrap()); 17 | buff.push(bm.pin(&BlockId::new("testfile", 2)).unwrap()); 18 | bm.unpin(buff[1]); 19 | buff[1] = 10; 20 | buff.push(bm.pin(&BlockId::new("testfile", 0)).unwrap()); 21 | buff.push(bm.pin(&BlockId::new("testfile", 1)).unwrap()); 22 | assert_eq!(0, bm.available()); 23 | 24 | assert!(bm.pin(&BlockId::new("testfile", 3)).is_err()); 25 | 26 | bm.unpin(buff[2]); 27 | buff[2] = 10; 28 | buff.push(bm.pin(&BlockId::new("testfile", 3)).unwrap()); 29 | 30 | let exp = HashMap::from([ 31 | (0, BlockId::new("testfile", 0)), 32 | (3, BlockId::new("testfile", 0)), 33 | (4, BlockId::new("testfile", 1)), 34 | (5, BlockId::new("testfile", 3)), 35 | ]); 36 | for (i, idx) in buff.iter().enumerate() { 37 | if *idx != 10 { 38 | let b = bm.buffer(*idx); 39 | assert_eq!(exp.get(&i).unwrap(), b.block().as_ref().unwrap()); 40 | } else { 41 | assert!(i == 1 || i == 2); 42 | } 43 | } 44 | 45 | fs::remove_dir_all("buffermgrtest").unwrap(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/buffer/buffertest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::fs; 4 | 5 | use crate::{file::blockid::BlockId, server::simpledb::SimpleDB}; 6 | 7 | #[test] 8 | fn buffertest() { 9 | let mut db = SimpleDB::with_params("buffertest", 400, 3).unwrap(); 10 | let m = db.buffer_mgr(); 11 | let mut bm = m.lock().unwrap(); 12 | 13 | let idx1 = bm.pin(&BlockId::new("testfile", 1)).unwrap(); 14 | let buff1 = bm.buffer(idx1); 15 | let p = buff1.contents(); 16 | let n = p.get_int(80); 17 | p.set_int(80, n + 1); 18 | buff1.set_modified(1, Some(0)); 19 | assert_eq!(1, n + 1); 20 | bm.unpin(idx1); 21 | 22 | let mut idx2 = bm.pin(&BlockId::new("testfile", 2)).unwrap(); 23 | bm.pin(&BlockId::new("testfile", 3)).unwrap(); 24 | bm.pin(&BlockId::new("testfile", 4)).unwrap(); 25 | 26 | bm.unpin(idx2); 27 | idx2 = bm.pin(&BlockId::new("testfile", 1)).unwrap(); 28 | let buff2 = bm.buffer(idx2); 29 | let p2 = buff2.contents(); 30 | p2.set_int(80, 9999); 31 | buff2.set_modified(1, Some(0)); 32 | bm.unpin(idx2); 33 | 34 | fs::remove_dir_all("buffertest").unwrap(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/file.rs: -------------------------------------------------------------------------------- 1 | pub mod blockid; 2 | pub mod filemgr; 3 | pub mod filetest; 4 | pub mod page; 5 | -------------------------------------------------------------------------------- /src/file/blockid.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 4 | pub struct BlockId { 5 | filename: String, 6 | blknum: i32, 7 | } 8 | 9 | impl Display for BlockId { 10 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 11 | write!(f, "[file {}, block {}]", self.filename, self.blknum) 12 | } 13 | } 14 | 15 | impl BlockId { 16 | pub fn new(filename: &str, blknum: i32) -> BlockId { 17 | BlockId { 18 | filename: filename.to_string(), 19 | blknum, 20 | } 21 | } 22 | 23 | pub fn file_name(&self) -> &str { 24 | &self.filename 25 | } 26 | 27 | pub fn number(&self) -> i32 { 28 | self.blknum 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/file/filemgr.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File, OpenOptions}; 2 | use std::io::{Read, SeekFrom, Write}; 3 | use std::path::Path; 4 | use std::{io::Error, io::Seek}; 5 | 6 | use super::blockid::BlockId; 7 | use super::page::Page; 8 | 9 | pub struct FileMgr { 10 | db_directory: String, 11 | blocksize: usize, 12 | is_new: bool, 13 | } 14 | 15 | impl FileMgr { 16 | pub fn new(db_directory: &str, blocksize: usize) -> Result { 17 | let is_new = !Path::new(db_directory).exists(); 18 | let fm = FileMgr { 19 | db_directory: db_directory.to_string(), 20 | blocksize, 21 | is_new, 22 | }; 23 | if fm.is_new { 24 | fs::create_dir_all(db_directory)?; 25 | } 26 | 27 | for entry in fs::read_dir(db_directory)? { 28 | let path = entry?.path(); 29 | if path.starts_with("temp") { 30 | fs::remove_file(path)?; 31 | } 32 | } 33 | Ok(fm) 34 | } 35 | 36 | pub fn read(&self, blk: &BlockId, p: &mut Page) -> Result<(), Error> { 37 | let mut f = self.get_file(blk.file_name())?; 38 | let pos = (blk.number() as usize * self.blocksize) as u64; 39 | f.seek(SeekFrom::Start(pos))?; 40 | if f.metadata()?.len() >= pos + p.contents().len() as u64 { 41 | f.read_exact(p.contents())?; 42 | } 43 | Ok(()) 44 | } 45 | 46 | pub fn write(&self, blk: &BlockId, p: &mut Page) -> Result<(), Error> { 47 | let mut f = self.get_file(blk.file_name())?; 48 | f.seek(SeekFrom::Start( 49 | (blk.number() as usize * self.blocksize) as u64, 50 | ))?; 51 | f.write_all(p.contents())?; 52 | Ok(()) 53 | } 54 | 55 | pub fn append(&self, filename: &str) -> Result { 56 | let newblknum = self.length(filename)?; 57 | let blk = BlockId::new(filename, newblknum as i32); 58 | let b: Vec = vec![0; self.blocksize]; 59 | 60 | let mut f = self.get_file(blk.file_name())?; 61 | f.seek(SeekFrom::Start( 62 | (blk.number() as usize * self.blocksize) as u64, 63 | ))?; 64 | f.write_all(&b)?; 65 | 66 | Ok(blk) 67 | } 68 | 69 | pub fn length(&self, filename: &str) -> Result { 70 | let file = self.get_file(filename)?; 71 | let metadata = file.metadata()?; 72 | Ok(metadata.len() as usize / self.blocksize) 73 | } 74 | 75 | pub fn is_new(&self) -> bool { 76 | self.is_new 77 | } 78 | 79 | pub fn block_size(&self) -> usize { 80 | self.blocksize 81 | } 82 | 83 | fn get_file(&self, filename: &str) -> Result { 84 | let filename = Path::new(&self.db_directory).join(filename); 85 | let file = OpenOptions::new() 86 | .read(true) 87 | .write(true) 88 | .create(true) 89 | .open(filename)?; 90 | 91 | Ok(file) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/file/filetest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::fs; 4 | 5 | use crate::{ 6 | file::{blockid::BlockId, page::Page}, 7 | server::simpledb::SimpleDB, 8 | }; 9 | 10 | #[test] 11 | fn filetest() { 12 | let db = SimpleDB::with_params("filetest", 400, 8).unwrap(); 13 | let fm = db.file_mgr(); 14 | 15 | let blk = BlockId::new("testfile", 2); 16 | let mut p1 = Page::new(fm.block_size()); 17 | let pos1 = 88; 18 | p1.set_string(pos1, "abcdefghijklm"); 19 | let size = Page::max_length("abcdefghijklm".len()); 20 | let pos2 = pos1 + size; 21 | p1.set_int(pos2, 345); 22 | fm.write(&blk, &mut p1).unwrap(); 23 | 24 | let mut p2 = Page::new(fm.block_size()); 25 | fm.read(&blk, &mut p2).unwrap(); 26 | 27 | assert_eq!(105, pos2); 28 | assert_eq!(345, p2.get_int(pos2)); 29 | assert_eq!(88, pos1); 30 | assert_eq!("abcdefghijklm", p2.get_string(pos1).unwrap()); 31 | 32 | fs::remove_dir_all("filetest").unwrap(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/file/page.rs: -------------------------------------------------------------------------------- 1 | use std::string::FromUtf8Error; 2 | 3 | pub struct Page { 4 | bb: Vec, 5 | } 6 | 7 | impl Page { 8 | pub fn new(blocksize: usize) -> Page { 9 | let mut vec: Vec = Vec::new(); 10 | vec.resize(blocksize, 0); 11 | Page { bb: vec } 12 | } 13 | 14 | pub fn with_vec(b: Vec) -> Page { 15 | Page { bb: b } 16 | } 17 | 18 | pub fn get_int(&self, offset: usize) -> i32 { 19 | let mut buf: [u8; 4] = [0; 4]; 20 | buf.copy_from_slice(&self.bb[offset..offset + 4]); 21 | i32::from_be_bytes(buf) 22 | } 23 | 24 | pub fn set_int(&mut self, offset: usize, n: i32) { 25 | self.bb.as_mut_slice()[offset..offset + 4].copy_from_slice(&n.to_be_bytes()); 26 | } 27 | 28 | pub fn get_bytes(&self, offset: usize) -> &[u8] { 29 | let len = self.get_int(offset) as usize; 30 | &self.bb[offset + 4..offset + 4 + len] 31 | } 32 | 33 | pub fn set_bytes(&mut self, offset: usize, b: &[u8]) { 34 | self.bb.as_mut_slice()[offset..offset + 4].copy_from_slice(&(b.len() as i32).to_be_bytes()); 35 | self.bb.as_mut_slice()[offset + 4..offset + 4 + b.len()].copy_from_slice(b); 36 | } 37 | 38 | pub fn get_string(&self, offset: usize) -> Result { 39 | let mut buf: Vec = Vec::new(); 40 | buf.extend_from_slice(self.get_bytes(offset)); 41 | String::from_utf8(buf) 42 | } 43 | 44 | pub fn set_string(&mut self, offset: usize, s: &str) { 45 | self.set_bytes(offset, s.as_bytes()); 46 | } 47 | 48 | pub fn max_length(strlen: usize) -> usize { 49 | let bytes_per_char = 1; 50 | 4 + strlen * bytes_per_char 51 | } 52 | 53 | pub(in crate) fn contents(&mut self) -> &mut Vec { 54 | &mut self.bb 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/index.rs: -------------------------------------------------------------------------------- 1 | pub mod btree; 2 | pub mod hash; 3 | pub mod index; 4 | pub mod indexretrievaltest; 5 | pub mod indexupdatetest; 6 | pub mod planner; 7 | pub mod query; 8 | -------------------------------------------------------------------------------- /src/index/btree.rs: -------------------------------------------------------------------------------- 1 | pub mod btpage; 2 | pub mod btreedir; 3 | pub mod btreeindex; 4 | pub mod btreeleaf; 5 | pub mod direntry; 6 | -------------------------------------------------------------------------------- /src/index/btree/direntry.rs: -------------------------------------------------------------------------------- 1 | use crate::query::constant::Constant; 2 | 3 | pub struct DirEntry { 4 | dataval: Constant, 5 | blocknum: i32, 6 | } 7 | 8 | impl DirEntry { 9 | pub fn new(dataval: Constant, blocknum: i32) -> DirEntry { 10 | DirEntry { dataval, blocknum } 11 | } 12 | 13 | pub fn data_val(&self) -> Constant { 14 | self.dataval.clone() 15 | } 16 | 17 | pub fn block_number(&self) -> i32 { 18 | self.blocknum 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/index/hash.rs: -------------------------------------------------------------------------------- 1 | pub mod hashindex; 2 | -------------------------------------------------------------------------------- /src/index/index.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use crate::{ 4 | buffer::buffermgr::AbortError, query::constant::Constant, record::rid::Rid, 5 | tx::transaction::TransactionError, 6 | }; 7 | 8 | use super::{btree::btreeindex::BTreeIndex, hash::hashindex::HashIndex}; 9 | 10 | #[enum_dispatch(Index)] 11 | pub trait IndexControl { 12 | fn before_first(&mut self, searchkey: Constant) -> Result<(), TransactionError>; 13 | fn next(&mut self) -> Result; 14 | fn get_data_rid(&mut self) -> Result; 15 | fn insert(&mut self, val: Constant, rid: &Rid) -> Result<(), TransactionError>; 16 | fn delete(&mut self, val: Constant, rid: &Rid) -> Result<(), TransactionError>; 17 | fn close(&mut self) -> Result<(), AbortError>; 18 | } 19 | 20 | #[enum_dispatch] 21 | pub enum Index { 22 | Hash(HashIndex), 23 | BTree(BTreeIndex), 24 | } 25 | -------------------------------------------------------------------------------- /src/index/indexretrievaltest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | collections::HashSet, 5 | fs, 6 | sync::{Arc, Mutex}, 7 | }; 8 | 9 | use crate::{ 10 | api::{ 11 | connection::ConnectionControl, driver::DriverControl, 12 | embedded::embeddeddriver::EmbeddedDriver, statement::StatementControl, 13 | }, 14 | index::index::IndexControl, 15 | plan::{plan::PlanControl, tableplan::TablePlan}, 16 | query::{ 17 | constant::Constant, 18 | scan::{Scan, ScanControl}, 19 | updatescan::UpdateScanControl, 20 | }, 21 | server::simpledb::SimpleDB, 22 | }; 23 | 24 | #[test] 25 | fn indexretrievaltest() { 26 | create_student_db(); 27 | 28 | let db = SimpleDB::new("indexretrievaltest").unwrap(); 29 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 30 | let mdm = db.md_mgr().unwrap(); 31 | 32 | let studentplan = TablePlan::new(tx.clone(), "student", mdm.clone()).unwrap(); 33 | let mut studentscan = match studentplan.open().unwrap() { 34 | Scan::Table(s) => s, 35 | _ => unreachable!(), 36 | }; 37 | 38 | let indexes = mdm 39 | .lock() 40 | .unwrap() 41 | .get_index_info("student", tx.clone()) 42 | .unwrap(); 43 | let ii = indexes.get("majorid").unwrap(); 44 | let mut idx = ii.open().unwrap(); 45 | 46 | let mut snames = HashSet::from(["amy", "kim", "pat", "sue"]); 47 | idx.before_first(Constant::with_int(20)).unwrap(); 48 | while idx.next().unwrap() { 49 | let datarid = idx.get_data_rid().unwrap(); 50 | studentscan.move_to_rid(&datarid).unwrap(); 51 | let sname = studentscan.get_string("sname").unwrap(); 52 | assert!(snames.remove(&*sname)); 53 | } 54 | assert!(snames.is_empty()); 55 | 56 | idx.close().unwrap(); 57 | studentscan.close().unwrap(); 58 | tx.lock().unwrap().commit().unwrap(); 59 | 60 | fs::remove_dir_all("indexretrievaltest").unwrap(); 61 | } 62 | 63 | fn create_student_db() { 64 | let d = EmbeddedDriver::new(); 65 | let mut conn = d.connect("indexretrievaltest").unwrap(); 66 | let mut stmt = conn.create_statement(); 67 | 68 | let s = "create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int)"; 69 | stmt.execute_update(s).unwrap(); 70 | 71 | let s = "create index idx on STUDENT(MajorId)"; 72 | stmt.execute_update(s).unwrap(); 73 | 74 | let s = "insert into STUDENT(SId, SName, MajorId, GradYear) values "; 75 | let studvals = [ 76 | "(1, 'joe', 10, 2021)", 77 | "(2, 'amy', 20, 2020)", 78 | "(3, 'max', 10, 2022)", 79 | "(4, 'sue', 20, 2022)", 80 | "(5, 'bob', 30, 2020)", 81 | "(6, 'kim', 20, 2020)", 82 | "(7, 'art', 30, 2021)", 83 | "(8, 'pat', 20, 2019)", 84 | "(9, 'lee', 10, 2021)", 85 | ]; 86 | for studval in studvals { 87 | stmt.execute_update(&format!("{}{}", s, studval)).unwrap(); 88 | } 89 | 90 | conn.close().unwrap(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/index/planner.rs: -------------------------------------------------------------------------------- 1 | pub mod indexjoinplan; 2 | pub mod indexselectplan; 3 | pub mod indexupdateplanner; 4 | -------------------------------------------------------------------------------- /src/index/planner/indexjoinplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{ 4 | index::query::indexjoinscan::IndexJoinScan, 5 | metadata::indexinfo::IndexInfo, 6 | plan::plan::{Plan, PlanControl}, 7 | query::scan::Scan, 8 | record::schema::Schema, 9 | tx::transaction::TransactionError, 10 | }; 11 | 12 | #[derive(Clone)] 13 | pub struct IndexJoinPlan { 14 | p1: Box, 15 | p2: Box, 16 | ii: IndexInfo, 17 | joinfield: String, 18 | sch: Arc, 19 | } 20 | 21 | impl IndexJoinPlan { 22 | pub fn new(p1: Plan, p2: Plan, ii: IndexInfo, joinfield: &str) -> IndexJoinPlan { 23 | let mut sch = Schema::new(); 24 | sch.add_all(&p1.schema()); 25 | sch.add_all(&p2.schema()); 26 | IndexJoinPlan { 27 | p1: Box::new(p1), 28 | p2: Box::new(p2), 29 | ii, 30 | joinfield: joinfield.to_string(), 31 | sch: Arc::new(sch), 32 | } 33 | } 34 | } 35 | 36 | impl PlanControl for IndexJoinPlan { 37 | fn open(&self) -> Result { 38 | let s = self.p1.open()?; 39 | if let Scan::Table(ts) = self.p2.open()? { 40 | let idx = self.ii.open()?; 41 | return Ok(IndexJoinScan::new(s, idx, &self.joinfield, ts)?.into()); 42 | } 43 | Err(TransactionError::General) 44 | } 45 | 46 | fn blocks_accessed(&self) -> usize { 47 | self.p1.blocks_accessed() 48 | + (self.p1.records_output() * self.ii.blocks_accessed()) 49 | + self.records_output() 50 | } 51 | 52 | fn records_output(&self) -> usize { 53 | self.p1.records_output() * self.ii.records_output() 54 | } 55 | 56 | fn distinct_values(&self, fldname: &str) -> usize { 57 | if self.p1.schema().has_field(fldname) { 58 | return self.p1.distinct_values(fldname); 59 | } 60 | self.p2.distinct_values(fldname) 61 | } 62 | 63 | fn schema(&self) -> Arc { 64 | self.sch.clone() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/index/planner/indexselectplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{ 4 | index::query::indexselectscan::IndexSelectScan, 5 | metadata::indexinfo::IndexInfo, 6 | plan::plan::{Plan, PlanControl}, 7 | query::{constant::Constant, scan::Scan}, 8 | record::schema::Schema, 9 | tx::transaction::TransactionError, 10 | }; 11 | 12 | #[derive(Clone)] 13 | pub struct IndexSelectPlan { 14 | p: Box, 15 | ii: IndexInfo, 16 | val: Constant, 17 | } 18 | 19 | impl IndexSelectPlan { 20 | pub fn new(p: Plan, ii: IndexInfo, val: Constant) -> IndexSelectPlan { 21 | IndexSelectPlan { 22 | p: Box::new(p), 23 | ii, 24 | val, 25 | } 26 | } 27 | } 28 | 29 | impl PlanControl for IndexSelectPlan { 30 | fn open(&self) -> Result { 31 | let s = self.p.open()?; 32 | if let Scan::Table(ts) = s { 33 | let idx = self.ii.open()?; 34 | return Ok(IndexSelectScan::new(ts, idx, self.val.clone())?.into()); 35 | } 36 | Err(TransactionError::General) 37 | } 38 | 39 | fn blocks_accessed(&self) -> usize { 40 | self.ii.blocks_accessed() + self.records_output() 41 | } 42 | 43 | fn records_output(&self) -> usize { 44 | self.ii.records_output() 45 | } 46 | 47 | fn distinct_values(&self, fldname: &str) -> usize { 48 | self.ii.distinct_values(fldname) 49 | } 50 | 51 | fn schema(&self) -> Arc { 52 | self.p.schema() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/index/query.rs: -------------------------------------------------------------------------------- 1 | pub mod indexjoinscan; 2 | pub mod indexjointest; 3 | pub mod indexselectscan; 4 | pub mod indexselecttest; 5 | -------------------------------------------------------------------------------- /src/index/query/indexjoinscan.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | buffer::buffermgr::AbortError, 3 | index::index::{Index, IndexControl}, 4 | query::{ 5 | constant::Constant, 6 | scan::{Scan, ScanControl}, 7 | updatescan::UpdateScanControl, 8 | }, 9 | record::tablescan::TableScan, 10 | tx::transaction::TransactionError, 11 | }; 12 | 13 | pub struct IndexJoinScan { 14 | lhs: Box, 15 | idx: Index, 16 | joinfield: String, 17 | rhs: TableScan, 18 | } 19 | 20 | impl IndexJoinScan { 21 | pub fn new( 22 | lhs: Scan, 23 | idx: Index, 24 | joinfield: &str, 25 | rhs: TableScan, 26 | ) -> Result { 27 | let mut s = IndexJoinScan { 28 | lhs: Box::new(lhs), 29 | idx, 30 | joinfield: joinfield.to_string(), 31 | rhs, 32 | }; 33 | s.before_first()?; 34 | Ok(s) 35 | } 36 | 37 | fn reset_index(&mut self) -> Result<(), TransactionError> { 38 | let searchkey = self.lhs.get_val(&self.joinfield)?; 39 | self.idx.before_first(searchkey)?; 40 | Ok(()) 41 | } 42 | } 43 | 44 | impl ScanControl for IndexJoinScan { 45 | fn before_first(&mut self) -> Result<(), TransactionError> { 46 | self.lhs.before_first()?; 47 | self.lhs.next()?; 48 | self.reset_index()?; 49 | Ok(()) 50 | } 51 | 52 | fn next(&mut self) -> Result { 53 | loop { 54 | if self.idx.next()? { 55 | self.rhs.move_to_rid(&self.idx.get_data_rid()?)?; 56 | return Ok(true); 57 | } 58 | if !self.lhs.next()? { 59 | return Ok(false); 60 | } 61 | self.reset_index()?; 62 | } 63 | } 64 | 65 | fn get_int(&mut self, fldname: &str) -> Result { 66 | if self.rhs.has_field(fldname) { 67 | return self.rhs.get_int(fldname); 68 | } 69 | self.lhs.get_int(fldname) 70 | } 71 | 72 | fn get_val(&mut self, fldname: &str) -> Result { 73 | if self.rhs.has_field(fldname) { 74 | return self.rhs.get_val(fldname); 75 | } 76 | self.lhs.get_val(fldname) 77 | } 78 | 79 | fn get_string(&mut self, fldname: &str) -> Result { 80 | if self.rhs.has_field(fldname) { 81 | return self.rhs.get_string(fldname); 82 | } 83 | self.lhs.get_string(fldname) 84 | } 85 | 86 | fn has_field(&self, fldname: &str) -> bool { 87 | self.rhs.has_field(fldname) || self.lhs.has_field(fldname) 88 | } 89 | 90 | fn close(&mut self) -> Result<(), AbortError> { 91 | self.lhs.close()?; 92 | self.idx.close()?; 93 | self.rhs.close()?; 94 | Ok(()) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/index/query/indexselectscan.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | buffer::buffermgr::AbortError, 3 | index::index::{Index, IndexControl}, 4 | query::{constant::Constant, scan::ScanControl, updatescan::UpdateScanControl}, 5 | record::tablescan::TableScan, 6 | tx::transaction::TransactionError, 7 | }; 8 | 9 | pub struct IndexSelectScan { 10 | ts: TableScan, 11 | idx: Index, 12 | val: Constant, 13 | } 14 | 15 | impl IndexSelectScan { 16 | pub fn new( 17 | ts: TableScan, 18 | idx: Index, 19 | val: Constant, 20 | ) -> Result { 21 | let mut s = IndexSelectScan { ts, idx, val }; 22 | s.before_first()?; 23 | Ok(s) 24 | } 25 | } 26 | 27 | impl ScanControl for IndexSelectScan { 28 | fn before_first(&mut self) -> Result<(), TransactionError> { 29 | self.idx.before_first(self.val.clone()) 30 | } 31 | 32 | fn next(&mut self) -> Result { 33 | let ok = self.idx.next()?; 34 | if ok { 35 | let rid = self.idx.get_data_rid()?; 36 | self.ts.move_to_rid(&rid)?; 37 | } 38 | Ok(ok) 39 | } 40 | 41 | fn get_int(&mut self, fldname: &str) -> Result { 42 | self.ts.get_int(fldname) 43 | } 44 | 45 | fn get_string(&mut self, fldname: &str) -> Result { 46 | self.ts.get_string(fldname) 47 | } 48 | 49 | fn get_val(&mut self, fldname: &str) -> Result { 50 | self.ts.get_val(fldname) 51 | } 52 | 53 | fn has_field(&self, fldname: &str) -> bool { 54 | self.ts.has_field(fldname) 55 | } 56 | 57 | fn close(&mut self) -> Result<(), AbortError> { 58 | self.idx.close()?; 59 | self.ts.close()?; 60 | Ok(()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | pub mod logiterator; 2 | pub mod logmgr; 3 | pub mod logtest; 4 | -------------------------------------------------------------------------------- /src/log/logiterator.rs: -------------------------------------------------------------------------------- 1 | use std::{io::Error, sync::Arc}; 2 | 3 | use crate::file::{blockid::BlockId, filemgr::FileMgr, page::Page}; 4 | 5 | pub struct LogIterator { 6 | fm: Arc, 7 | blk: BlockId, 8 | p: Page, 9 | currentpos: i32, 10 | boundary: i32, 11 | } 12 | 13 | impl LogIterator { 14 | pub fn new(fm: Arc, blk: &BlockId) -> Result { 15 | let b: Vec = vec![0; fm.block_size()]; 16 | let p = Page::with_vec(b); 17 | let mut l = LogIterator { 18 | fm, 19 | blk: blk.clone(), 20 | p, 21 | currentpos: 0, 22 | boundary: 0, 23 | }; 24 | l.move_to_block(blk)?; 25 | Ok(l) 26 | } 27 | 28 | fn move_to_block(&mut self, blk: &BlockId) -> Result<(), Error> { 29 | self.fm.clone().read(blk, &mut self.p)?; 30 | self.boundary = self.p.get_int(0); 31 | self.currentpos = self.boundary; 32 | Ok(()) 33 | } 34 | } 35 | 36 | impl Iterator for LogIterator { 37 | type Item = Vec; 38 | 39 | fn next(&mut self) -> Option { 40 | if self.currentpos >= self.fm.block_size() as i32 && self.blk.number() <= 0 { 41 | return None; 42 | } 43 | if self.currentpos == self.fm.block_size() as i32 { 44 | self.blk = BlockId::new(self.blk.file_name(), self.blk.number() - 1); 45 | self.move_to_block(&self.blk.clone()).ok(); 46 | } 47 | let rec = self.p.get_bytes(self.currentpos as usize); 48 | self.currentpos += 4 + rec.len() as i32; 49 | Some(rec.to_vec()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/log/logmgr.rs: -------------------------------------------------------------------------------- 1 | use std::{io::Error, sync::Arc}; 2 | 3 | use crate::file::{blockid::BlockId, filemgr::FileMgr, page::Page}; 4 | 5 | use super::logiterator::LogIterator; 6 | 7 | pub struct LogMgr { 8 | fm: Arc, 9 | logfile: String, 10 | logpage: Page, 11 | currentblk: BlockId, 12 | lastest_lsn: usize, 13 | last_saved_lsn: usize, 14 | } 15 | 16 | fn append_new_block(fm: &FileMgr, logpage: &mut Page, logfile: &str) -> Result { 17 | let blk = fm.append(logfile)?; 18 | logpage.set_int(0, fm.block_size() as i32); 19 | fm.write(&blk, logpage)?; 20 | Ok(blk) 21 | } 22 | 23 | impl LogMgr { 24 | pub fn new(fm: Arc, logfile: &str) -> Result { 25 | let b = vec![0; fm.block_size()]; 26 | let mut logpage = Page::with_vec(b); 27 | let logsize = fm.length(logfile)?; 28 | let currentblk = if logsize == 0 { 29 | append_new_block(&fm, &mut logpage, logfile)? 30 | } else { 31 | let currentblk = BlockId::new(logfile, logsize as i32 - 1); 32 | fm.read(¤tblk, &mut logpage)?; 33 | currentblk 34 | }; 35 | Ok(LogMgr { 36 | fm, 37 | logfile: logfile.to_string(), 38 | logpage, 39 | currentblk, 40 | lastest_lsn: 0, 41 | last_saved_lsn: 0, 42 | }) 43 | } 44 | 45 | pub fn flush(&mut self, lsn: usize) -> Result<(), Error> { 46 | if lsn >= self.last_saved_lsn { 47 | self.flush_impl()? 48 | } 49 | Ok(()) 50 | } 51 | 52 | pub fn iterator(&mut self) -> Result { 53 | self.flush_impl()?; 54 | LogIterator::new(self.fm.clone(), &self.currentblk) 55 | } 56 | 57 | pub fn append(&mut self, logrec: &Vec) -> Result { 58 | let mut boundary = self.logpage.get_int(0); 59 | let recsize = logrec.len() as i32; 60 | let bytesneeded = recsize + 4; 61 | if boundary - bytesneeded < 4 { 62 | self.flush_impl()?; 63 | self.currentblk = self.append_new_block()?; 64 | boundary = self.logpage.get_int(0); 65 | } 66 | let recpos = boundary - bytesneeded; 67 | self.logpage.set_bytes(recpos as usize, logrec); 68 | self.logpage.set_int(0, recpos); 69 | self.lastest_lsn += 1; 70 | Ok(self.lastest_lsn) 71 | } 72 | 73 | fn append_new_block(&mut self) -> Result { 74 | append_new_block(&self.fm, &mut self.logpage, self.logfile.as_str()) 75 | } 76 | 77 | fn flush_impl(&mut self) -> Result<(), Error> { 78 | self.fm.write(&self.currentblk, &mut self.logpage)?; 79 | self.last_saved_lsn = self.lastest_lsn; 80 | Ok(()) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/log/logtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | iter::zip, 6 | sync::{Arc, Mutex}, 7 | }; 8 | 9 | use crate::{file::page::Page, log::logmgr::LogMgr, server::simpledb::SimpleDB}; 10 | 11 | #[test] 12 | fn logtest() { 13 | let mut db = SimpleDB::with_params("logtest", 400, 8).unwrap(); 14 | let lm = db.log_mgr(); 15 | 16 | assert_log_records(lm.clone(), Vec::new()); 17 | create_records(lm.clone(), 1, 35); 18 | assert_log_records(lm.clone(), (1..=35).rev().collect()); 19 | create_records(lm.clone(), 36, 70); 20 | lm.lock().unwrap().flush(65).unwrap(); 21 | assert_log_records(lm, (1..=70).rev().collect()); 22 | 23 | fs::remove_dir_all("logtest").unwrap(); 24 | } 25 | 26 | fn assert_log_records(lm: Arc>, expected: Vec) { 27 | let iter = lm.lock().unwrap().iterator().unwrap(); 28 | for (rec, exp) in zip(iter, expected) { 29 | let p = Page::with_vec(rec); 30 | let s = p.get_string(0).unwrap(); 31 | let npos = Page::max_length(s.len()); 32 | let val = p.get_int(npos); 33 | 34 | assert_eq!(format!("record{}", exp), s); 35 | assert_eq!(exp + 100, val); 36 | } 37 | } 38 | 39 | fn create_records(lm: Arc>, start: usize, end: usize) { 40 | for i in start..=end { 41 | let s = format!("{}{}", "record", i); 42 | let rec = create_log_record(s.as_str(), i + 100); 43 | let lsn = lm.lock().unwrap().append(&rec).unwrap(); 44 | assert_eq!(i, lsn); 45 | } 46 | } 47 | 48 | fn create_log_record(s: &str, n: usize) -> Vec { 49 | let spos = 0; 50 | let npos = Page::max_length(s.len()); 51 | let b: Vec = vec![0; npos + 4]; 52 | let mut p = Page::with_vec(b); 53 | p.set_string(spos, s); 54 | p.set_int(npos, n as i32); 55 | p.contents().to_vec() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/materialize.rs: -------------------------------------------------------------------------------- 1 | pub mod aggregationfn; 2 | pub mod countfn; 3 | pub mod groupbyplan; 4 | pub mod groupbyscan; 5 | pub mod groupbytest; 6 | pub mod groupvalue; 7 | pub mod materializeplan; 8 | pub mod maxfn; 9 | pub mod mergejoinplan; 10 | pub mod mergejoinscan; 11 | pub mod mergejointest; 12 | pub mod recordcomparator; 13 | pub mod sortplan; 14 | pub mod sortscan; 15 | pub mod sorttest; 16 | pub mod temptable; 17 | -------------------------------------------------------------------------------- /src/materialize/aggregationfn.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use enum_dispatch::enum_dispatch; 4 | 5 | use crate::{ 6 | query::{constant::Constant, scan::Scan}, 7 | tx::transaction::TransactionError, 8 | }; 9 | 10 | use super::{countfn::CountFn, maxfn::MaxFn}; 11 | 12 | #[enum_dispatch(AggregationFn)] 13 | pub trait AggregationFnControl { 14 | fn process_first(&mut self, s: Arc>) -> Result<(), TransactionError>; 15 | fn process_next(&mut self, s: Arc>) -> Result<(), TransactionError>; 16 | fn field_name(&self) -> String; 17 | fn value(&self) -> Option; 18 | } 19 | 20 | #[derive(Clone)] 21 | #[enum_dispatch] 22 | pub enum AggregationFn { 23 | Max(MaxFn), 24 | Count(CountFn), 25 | } 26 | -------------------------------------------------------------------------------- /src/materialize/countfn.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | query::{constant::Constant, scan::Scan}, 5 | tx::transaction::TransactionError, 6 | }; 7 | 8 | use super::aggregationfn::AggregationFnControl; 9 | 10 | #[derive(Clone)] 11 | pub struct CountFn { 12 | fldname: String, 13 | count: usize, 14 | } 15 | 16 | impl AggregationFnControl for CountFn { 17 | fn process_first(&mut self, _: Arc>) -> Result<(), TransactionError> { 18 | self.count = 1; 19 | Ok(()) 20 | } 21 | 22 | fn process_next(&mut self, _: Arc>) -> Result<(), TransactionError> { 23 | self.count += 1; 24 | Ok(()) 25 | } 26 | 27 | fn field_name(&self) -> String { 28 | format!("countof{}", self.fldname) 29 | } 30 | 31 | fn value(&self) -> Option { 32 | Some(Constant::with_int(self.count as i32)) 33 | } 34 | } 35 | 36 | impl CountFn { 37 | #[allow(dead_code)] 38 | pub fn new(fldname: &str) -> CountFn { 39 | CountFn { 40 | fldname: fldname.to_string(), 41 | count: 0, 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/materialize/groupbyplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | plan::plan::{Plan, PlanControl}, 5 | query::scan::Scan, 6 | record::schema::Schema, 7 | tx::transaction::{Transaction, TransactionError}, 8 | }; 9 | 10 | use super::{ 11 | aggregationfn::{AggregationFn, AggregationFnControl}, 12 | groupbyscan::GroupByScan, 13 | sortplan::SortPlan, 14 | }; 15 | 16 | #[derive(Clone)] 17 | pub struct GroupByPlan { 18 | p: Box, 19 | groupfields: Vec, 20 | aggfns: Vec, 21 | sch: Arc, 22 | } 23 | 24 | impl PlanControl for GroupByPlan { 25 | fn open(&self) -> Result { 26 | let s = self.p.open()?; 27 | Ok(GroupByScan::new(s, self.groupfields.clone(), self.aggfns.clone())?.into()) 28 | } 29 | 30 | fn blocks_accessed(&self) -> usize { 31 | self.p.blocks_accessed() 32 | } 33 | 34 | fn records_output(&self) -> usize { 35 | let mut numgroups = 1; 36 | for fldname in &self.groupfields { 37 | numgroups *= self.p.distinct_values(fldname); 38 | } 39 | numgroups 40 | } 41 | 42 | fn distinct_values(&self, fldname: &str) -> usize { 43 | if self.p.schema().has_field(fldname) { 44 | return self.p.distinct_values(fldname); 45 | } 46 | self.records_output() 47 | } 48 | 49 | fn schema(&self) -> Arc { 50 | self.sch.clone() 51 | } 52 | } 53 | 54 | impl GroupByPlan { 55 | #[allow(dead_code)] 56 | pub fn new( 57 | tx: Arc>, 58 | p: Plan, 59 | groupfields: Vec, 60 | aggfns: Vec, 61 | ) -> GroupByPlan { 62 | let p = SortPlan::new(tx, p, groupfields.clone()); 63 | let mut sch = Schema::new(); 64 | for fldname in &groupfields { 65 | sch.add(fldname, p.schema().as_ref()); 66 | } 67 | for f in &aggfns { 68 | sch.add_int_field(&f.field_name()); 69 | } 70 | let p = Box::new(p); 71 | let sch = Arc::new(sch); 72 | GroupByPlan { 73 | p, 74 | groupfields, 75 | aggfns, 76 | sch, 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/materialize/groupbytest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | collections::HashSet, 5 | fs, 6 | sync::{Arc, Mutex}, 7 | }; 8 | 9 | use crate::{ 10 | api::{ 11 | connection::ConnectionControl, driver::DriverControl, 12 | embedded::embeddeddriver::EmbeddedDriver, statement::StatementControl, 13 | }, 14 | materialize::{countfn::CountFn, groupbyplan::GroupByPlan, maxfn::MaxFn}, 15 | plan::{plan::PlanControl, tableplan::TablePlan}, 16 | query::scan::ScanControl, 17 | server::simpledb::SimpleDB, 18 | }; 19 | 20 | #[test] 21 | fn groupbytest() { 22 | create_student_db(); 23 | 24 | let db = SimpleDB::new("groupbytest").unwrap(); 25 | let mdm = db.md_mgr().unwrap(); 26 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 27 | 28 | let p1 = TablePlan::new(tx.clone(), "student", mdm).unwrap(); 29 | 30 | let groupfields = vec!["majorid".to_string()]; 31 | let aggfns = vec![ 32 | MaxFn::new("gradyear").into(), 33 | CountFn::new("gradyear").into(), 34 | ]; 35 | let p2 = GroupByPlan::new(tx, p1.into(), groupfields, aggfns); 36 | 37 | let mut records = HashSet::from([(10, 2022, 3), (20, 2022, 4), (30, 2021, 2)]); 38 | let mut s = p2.open().unwrap(); 39 | while s.next().unwrap() { 40 | let record = ( 41 | s.get_int("majorid").unwrap(), 42 | s.get_int("maxofgradyear").unwrap(), 43 | s.get_int("countofgradyear").unwrap(), 44 | ); 45 | assert!(records.contains(&record)); 46 | records.remove(&record); 47 | } 48 | assert!(records.is_empty()); 49 | s.close().unwrap(); 50 | 51 | fs::remove_dir_all("groupbytest").unwrap(); 52 | } 53 | 54 | fn create_student_db() { 55 | let d = EmbeddedDriver::new(); 56 | let mut conn = d.connect("groupbytest").unwrap(); 57 | let mut stmt = conn.create_statement(); 58 | 59 | let s = "create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int)"; 60 | stmt.execute_update(s).unwrap(); 61 | 62 | let s = "insert into STUDENT(SId, SName, MajorId, GradYear) values "; 63 | let studvals = [ 64 | "(1, 'joe', 10, 2021)", 65 | "(2, 'amy', 20, 2020)", 66 | "(3, 'max', 10, 2022)", 67 | "(4, 'sue', 20, 2022)", 68 | "(5, 'bob', 30, 2020)", 69 | "(6, 'kim', 20, 2020)", 70 | "(7, 'art', 30, 2021)", 71 | "(8, 'pat', 20, 2019)", 72 | "(9, 'lee', 10, 2021)", 73 | ]; 74 | for studval in studvals { 75 | stmt.execute_update(&format!("{}{}", s, studval)).unwrap(); 76 | } 77 | 78 | conn.close().unwrap(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/materialize/groupvalue.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | query::{ 8 | constant::Constant, 9 | scan::{Scan, ScanControl}, 10 | }, 11 | tx::transaction::TransactionError, 12 | }; 13 | 14 | #[derive(Clone, PartialEq)] 15 | pub struct GroupValue { 16 | vals: HashMap, 17 | } 18 | 19 | impl GroupValue { 20 | pub fn new(s: Arc>, fields: Vec) -> Result { 21 | let mut vals = HashMap::new(); 22 | for fldname in &fields { 23 | vals.insert(fldname.clone(), s.lock().unwrap().get_val(fldname)?); 24 | } 25 | Ok(GroupValue { vals }) 26 | } 27 | 28 | pub fn get_val(&self, fldname: &str) -> Option<&Constant> { 29 | self.vals.get(fldname) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/materialize/materializeplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | plan::plan::{Plan, PlanControl}, 5 | query::{ 6 | scan::{Scan, ScanControl}, 7 | updatescan::UpdateScanControl, 8 | }, 9 | record::{layout::Layout, schema::Schema}, 10 | tx::transaction::{Transaction, TransactionError}, 11 | }; 12 | 13 | use super::temptable::TempTable; 14 | 15 | #[derive(Clone)] 16 | pub struct MaterializePlan { 17 | srcplan: Box, 18 | tx: Arc>, 19 | } 20 | 21 | impl MaterializePlan { 22 | pub fn new(tx: Arc>, srcplan: Plan) -> MaterializePlan { 23 | MaterializePlan { 24 | srcplan: Box::new(srcplan), 25 | tx, 26 | } 27 | } 28 | } 29 | 30 | impl PlanControl for MaterializePlan { 31 | fn open(&self) -> Result { 32 | let sch = self.srcplan.schema(); 33 | let temp = TempTable::new(self.tx.clone(), sch.clone()); 34 | let mut src = self.srcplan.open()?; 35 | let mut dest = temp.open()?; 36 | while src.next()? { 37 | dest.insert()?; 38 | for fldname in sch.fields() { 39 | dest.set_val(fldname, src.get_val(fldname)?)?; 40 | } 41 | } 42 | src.close()?; 43 | dest.before_first()?; 44 | Ok(Scan::Table(dest)) 45 | } 46 | 47 | fn blocks_accessed(&self) -> usize { 48 | let layout = Layout::new(self.srcplan.schema()); 49 | let rpb = self.tx.lock().unwrap().block_size() / layout.slot_size(); 50 | (self.srcplan.records_output() as f64 / rpb as f64).ceil() as usize 51 | } 52 | 53 | fn records_output(&self) -> usize { 54 | self.srcplan.records_output() 55 | } 56 | 57 | fn distinct_values(&self, fldname: &str) -> usize { 58 | self.srcplan.distinct_values(fldname) 59 | } 60 | 61 | fn schema(&self) -> Arc { 62 | self.srcplan.schema() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/materialize/maxfn.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | query::{ 5 | constant::Constant, 6 | scan::{Scan, ScanControl}, 7 | }, 8 | tx::transaction::TransactionError, 9 | }; 10 | 11 | use super::aggregationfn::AggregationFnControl; 12 | 13 | #[derive(Clone)] 14 | pub struct MaxFn { 15 | fldname: String, 16 | val: Option, 17 | } 18 | 19 | impl AggregationFnControl for MaxFn { 20 | fn process_first(&mut self, s: Arc>) -> Result<(), TransactionError> { 21 | self.val = Some(s.lock().unwrap().get_val(&self.fldname)?); 22 | Ok(()) 23 | } 24 | 25 | fn process_next(&mut self, s: Arc>) -> Result<(), TransactionError> { 26 | let newval = s.lock().unwrap().get_val(&self.fldname)?; 27 | if let Some(val) = &self.val { 28 | if newval > *val { 29 | self.val = Some(newval); 30 | } 31 | } 32 | Ok(()) 33 | } 34 | fn field_name(&self) -> String { 35 | format!("maxof{}", self.fldname) 36 | } 37 | 38 | fn value(&self) -> Option { 39 | self.val.clone() 40 | } 41 | } 42 | 43 | impl MaxFn { 44 | #[allow(dead_code)] 45 | pub fn new(fldname: &str) -> MaxFn { 46 | MaxFn { 47 | fldname: fldname.to_string(), 48 | val: None, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/materialize/mergejoinplan.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | plan::plan::{Plan, PlanControl}, 8 | query::scan::Scan, 9 | record::schema::Schema, 10 | tx::transaction::{Transaction, TransactionError}, 11 | }; 12 | 13 | use super::{mergejoinscan::MergeJoinScan, sortplan::SortPlan}; 14 | 15 | #[derive(Clone)] 16 | pub struct MergeJoinPlan { 17 | p1: Box, 18 | p2: Box, 19 | fldname1: String, 20 | fldname2: String, 21 | sch: Arc, 22 | } 23 | 24 | impl PlanControl for MergeJoinPlan { 25 | fn open(&self) -> Result { 26 | let s1 = self.p1.open()?; 27 | if let Scan::Sort(s2) = self.p2.open()? { 28 | return Ok(MergeJoinScan::new(s1, s2, &self.fldname1, &self.fldname2).into()); 29 | } 30 | Err(TransactionError::General) 31 | } 32 | 33 | fn blocks_accessed(&self) -> usize { 34 | self.p1.blocks_accessed() + self.p2.blocks_accessed() 35 | } 36 | 37 | fn records_output(&self) -> usize { 38 | let maxvals = cmp::max( 39 | self.p1.distinct_values(&self.fldname1), 40 | self.p2.distinct_values(&self.fldname2), 41 | ); 42 | (self.p1.records_output() * self.p2.records_output()) / maxvals 43 | } 44 | 45 | fn distinct_values(&self, fldname: &str) -> usize { 46 | if self.p1.schema().has_field(fldname) { 47 | return self.p1.distinct_values(fldname); 48 | } 49 | self.p2.distinct_values(fldname) 50 | } 51 | 52 | fn schema(&self) -> Arc { 53 | self.sch.clone() 54 | } 55 | } 56 | 57 | impl MergeJoinPlan { 58 | #[allow(dead_code)] 59 | pub fn new( 60 | tx: Arc>, 61 | p1: Plan, 62 | p2: Plan, 63 | fldname1: &str, 64 | fldname2: &str, 65 | ) -> MergeJoinPlan { 66 | let fldname1 = fldname1.to_string(); 67 | let sortlist1 = vec![fldname1.clone()]; 68 | let p1: Box = Box::new(SortPlan::new(tx.clone(), p1, sortlist1).into()); 69 | 70 | let fldname2 = fldname2.to_string(); 71 | let sortlist2 = vec![fldname2.clone()]; 72 | let p2: Box = Box::new(SortPlan::new(tx, p2, sortlist2).into()); 73 | 74 | let mut sch = Schema::new(); 75 | sch.add_all(&p1.schema()); 76 | sch.add_all(&p2.schema()); 77 | let sch = Arc::new(sch); 78 | 79 | MergeJoinPlan { 80 | p1, 81 | p2, 82 | fldname1, 83 | fldname2, 84 | sch, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/materialize/recordcomparator.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::Ordering, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::query::scan::ScanControl; 7 | 8 | #[derive(Clone)] 9 | pub struct RecordComparator { 10 | fields: Vec, 11 | } 12 | 13 | impl RecordComparator { 14 | pub fn new(fields: Vec) -> RecordComparator { 15 | RecordComparator { fields } 16 | } 17 | 18 | pub fn partial_cmp( 19 | &self, 20 | s1: Arc>, 21 | s2: Arc>, 22 | ) -> Option { 23 | for fldname in &self.fields { 24 | match ( 25 | s1.lock().unwrap().get_val(fldname), 26 | s2.lock().unwrap().get_val(fldname), 27 | ) { 28 | (Ok(val1), Ok(val2)) => { 29 | if val1 > val2 { 30 | return Some(Ordering::Greater); 31 | } else if val1 < val2 { 32 | return Some(Ordering::Less); 33 | } 34 | } 35 | _ => return None, 36 | } 37 | } 38 | Some(Ordering::Equal) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/materialize/sorttest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | api::{ 10 | connection::ConnectionControl, driver::DriverControl, 11 | embedded::embeddeddriver::EmbeddedDriver, statement::StatementControl, 12 | }, 13 | materialize::sortplan::SortPlan, 14 | plan::{plan::PlanControl, tableplan::TablePlan}, 15 | query::scan::ScanControl, 16 | server::simpledb::SimpleDB, 17 | }; 18 | 19 | #[test] 20 | fn sorttest() { 21 | create_student_db(); 22 | 23 | let db = SimpleDB::new("sorttest").unwrap(); 24 | let mdm = db.md_mgr().unwrap(); 25 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 26 | 27 | let p1 = TablePlan::new(tx.clone(), "student", mdm).unwrap(); 28 | 29 | let sortfields = vec!["gradyear".to_string(), "sname".to_string()]; 30 | let p2 = SortPlan::new(tx, p1.into(), sortfields); 31 | 32 | let students = [ 33 | ("pat", 2019), 34 | ("amy", 2020), 35 | ("bob", 2020), 36 | ("kim", 2020), 37 | ("art", 2021), 38 | ("joe", 2021), 39 | ("lee", 2021), 40 | ("max", 2022), 41 | ("sue", 2022), 42 | ]; 43 | let mut count = 0; 44 | let mut s = p2.open().unwrap(); 45 | while s.next().unwrap() { 46 | assert_eq!(s.get_string("sname").unwrap(), students[count].0); 47 | assert_eq!(s.get_int("gradyear").unwrap(), students[count].1); 48 | count += 1; 49 | } 50 | assert_eq!(count, students.len()); 51 | s.close().unwrap(); 52 | 53 | fs::remove_dir_all("sorttest").unwrap(); 54 | } 55 | 56 | fn create_student_db() { 57 | let d = EmbeddedDriver::new(); 58 | let mut conn = d.connect("sorttest").unwrap(); 59 | let mut stmt = conn.create_statement(); 60 | 61 | let s = "create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int)"; 62 | stmt.execute_update(s).unwrap(); 63 | 64 | let s = "insert into STUDENT(SId, SName, MajorId, GradYear) values "; 65 | let studvals = [ 66 | "(1, 'joe', 10, 2021)", 67 | "(2, 'amy', 20, 2020)", 68 | "(3, 'max', 10, 2022)", 69 | "(4, 'sue', 20, 2022)", 70 | "(5, 'bob', 30, 2020)", 71 | "(6, 'kim', 20, 2020)", 72 | "(7, 'art', 30, 2021)", 73 | "(8, 'pat', 20, 2019)", 74 | "(9, 'lee', 10, 2021)", 75 | ]; 76 | for studval in studvals { 77 | stmt.execute_update(&format!("{}{}", s, studval)).unwrap(); 78 | } 79 | 80 | conn.close().unwrap(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/materialize/temptable.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{ 2 | atomic::{AtomicUsize, Ordering}, 3 | Arc, Mutex, 4 | }; 5 | 6 | use crate::{ 7 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 8 | tx::transaction::{Transaction, TransactionError}, 9 | }; 10 | 11 | pub struct TempTable { 12 | tx: Arc>, 13 | tblname: String, 14 | layout: Layout, 15 | } 16 | 17 | static NEXT_TABLE_NUM: AtomicUsize = AtomicUsize::new(0); 18 | 19 | fn next_table_name() -> String { 20 | NEXT_TABLE_NUM.fetch_add(1, Ordering::SeqCst); 21 | format!("temp{}", NEXT_TABLE_NUM.load(Ordering::SeqCst)) 22 | } 23 | 24 | impl TempTable { 25 | pub fn new(tx: Arc>, sch: Arc) -> TempTable { 26 | let tblname = next_table_name(); 27 | let layout = Layout::new(sch); 28 | TempTable { 29 | tx, 30 | tblname, 31 | layout, 32 | } 33 | } 34 | 35 | pub fn open(&self) -> Result { 36 | TableScan::new(self.tx.clone(), &self.tblname, self.layout.clone()) 37 | } 38 | 39 | pub fn table_name(&self) -> String { 40 | self.tblname.clone() 41 | } 42 | 43 | pub fn get_layout(&self) -> Layout { 44 | self.layout.clone() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/metadata.rs: -------------------------------------------------------------------------------- 1 | pub mod catalogtest; 2 | pub mod indexinfo; 3 | pub mod indexmgr; 4 | pub mod metadatamgr; 5 | pub mod metadatamgrtest; 6 | pub mod statinfo; 7 | pub mod statmgr; 8 | pub mod tablemgr; 9 | pub mod tablemgrtest; 10 | pub mod viewmgr; 11 | -------------------------------------------------------------------------------- /src/metadata/catalogtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | metadata::tablemgr::TableMgr, query::scan::ScanControl, record::tablescan::TableScan, 10 | server::simpledb::SimpleDB, 11 | }; 12 | 13 | #[test] 14 | fn catalogtest() { 15 | let db = SimpleDB::with_params("catalogtest", 400, 8).unwrap(); 16 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 17 | let tm = TableMgr::new(true, tx.clone()).unwrap(); 18 | let tcat_layout = tm.get_layout("tblcat", tx.clone()).unwrap(); 19 | 20 | let mut ts = TableScan::new(tx.clone(), "tblcat", tcat_layout).unwrap(); 21 | let mut i = 0; 22 | let e = [("tblcat", 28), ("fldcat", 56)]; 23 | while ts.next().unwrap() { 24 | let tname = ts.get_string("tblname").unwrap(); 25 | let slotsize = ts.get_int("slotsize").unwrap(); 26 | assert_eq!(tname, e[i].0); 27 | assert_eq!(slotsize, e[i].1); 28 | i += 1; 29 | } 30 | assert_eq!(i, e.len()); 31 | ts.close().unwrap(); 32 | 33 | let fcat_layout = tm.get_layout("fldcat", tx.clone()).unwrap(); 34 | ts = TableScan::new(tx.clone(), "fldcat", fcat_layout).unwrap(); 35 | let mut i = 0; 36 | let e = [ 37 | ("tblcat", "tblname", 4), 38 | ("tblcat", "slotsize", 24), 39 | ("fldcat", "tblname", 4), 40 | ("fldcat", "fldname", 24), 41 | ("fldcat", "type", 44), 42 | ("fldcat", "length", 48), 43 | ("fldcat", "offset", 52), 44 | ]; 45 | while ts.next().unwrap() { 46 | let tname = ts.get_string("tblname").unwrap(); 47 | let fname = ts.get_string("fldname").unwrap(); 48 | let offset = ts.get_int("offset").unwrap(); 49 | assert_eq!(tname, e[i].0); 50 | assert_eq!(fname, e[i].1); 51 | assert_eq!(offset, e[i].2); 52 | i += 1; 53 | } 54 | assert_eq!(i, e.len()); 55 | ts.close().unwrap(); 56 | 57 | fs::remove_dir_all("catalogtest").unwrap(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/metadata/indexinfo.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | index::{btree::btreeindex::BTreeIndex, index::Index}, 5 | record::{ 6 | layout::Layout, 7 | schema::{Schema, Type}, 8 | }, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::statinfo::StatInfo; 13 | 14 | #[derive(Clone)] 15 | pub struct IndexInfo { 16 | idxname: String, 17 | fldname: String, 18 | tx: Arc>, 19 | idx_layout: Layout, 20 | si: StatInfo, 21 | } 22 | 23 | fn create_idx_layout(fldname: &str, tbl_schema: &Schema) -> Layout { 24 | let mut sch = Schema::new(); 25 | sch.add_int_field("block"); 26 | sch.add_int_field("id"); 27 | match tbl_schema.type_(fldname) { 28 | Type::Integer => sch.add_int_field("dataval"), 29 | Type::Varchar => { 30 | let fldlen = tbl_schema.length(fldname); 31 | sch.add_string_field("dataval", fldlen) 32 | } 33 | } 34 | Layout::new(Arc::new(sch)) 35 | } 36 | 37 | impl IndexInfo { 38 | pub fn new( 39 | idxname: &str, 40 | fldname: &str, 41 | tbl_schema: Arc, 42 | tx: Arc>, 43 | si: StatInfo, 44 | ) -> IndexInfo { 45 | let idx_layout = create_idx_layout(fldname, &tbl_schema); 46 | IndexInfo { 47 | idxname: idxname.to_string(), 48 | fldname: fldname.to_string(), 49 | tx, 50 | idx_layout, 51 | si, 52 | } 53 | } 54 | 55 | pub fn open(&self) -> Result { 56 | Ok(BTreeIndex::new(self.tx.clone(), &self.idxname, self.idx_layout.clone())?.into()) 57 | } 58 | 59 | pub fn blocks_accessed(&self) -> usize { 60 | let rpb = self.tx.lock().unwrap().block_size() / self.idx_layout.slot_size(); 61 | let numblocks = self.si.records_output() / rpb; 62 | BTreeIndex::search_cost(numblocks, rpb) 63 | } 64 | 65 | pub fn records_output(&self) -> usize { 66 | self.si.records_output() / self.si.distinct_values(&self.fldname) 67 | } 68 | 69 | pub fn distinct_values(&self, fname: &str) -> usize { 70 | if self.fldname == fname { 71 | return 1; 72 | } 73 | self.si.distinct_values(&self.fldname) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/metadata/indexmgr.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | query::{scan::ScanControl, updatescan::UpdateScanControl}, 8 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::{indexinfo::IndexInfo, statmgr::StatMgr, tablemgr::TableMgr}; 13 | 14 | pub struct IndexMgr { 15 | layout: Layout, 16 | tblmgr: Arc, 17 | statmgr: Arc>, 18 | } 19 | 20 | impl IndexMgr { 21 | pub fn new( 22 | isnew: bool, 23 | tblmgr: Arc, 24 | statmgr: Arc>, 25 | tx: Arc>, 26 | ) -> Result { 27 | if isnew { 28 | let mut sch = Schema::new(); 29 | sch.add_string_field("indexname", TableMgr::MAX_NAME); 30 | sch.add_string_field("tablename", TableMgr::MAX_NAME); 31 | sch.add_string_field("fieldname", TableMgr::MAX_NAME); 32 | tblmgr.create_table("idxcat", Arc::new(sch), tx.clone())?; 33 | } 34 | let layout = tblmgr.get_layout("idxcat", tx)?; 35 | Ok(IndexMgr { 36 | layout, 37 | tblmgr, 38 | statmgr, 39 | }) 40 | } 41 | 42 | pub fn create_index( 43 | &self, 44 | idxname: &str, 45 | tblname: &str, 46 | fldname: &str, 47 | tx: Arc>, 48 | ) -> Result<(), TransactionError> { 49 | let mut ts = TableScan::new(tx, "idxcat", self.layout.clone())?; 50 | ts.insert()?; 51 | ts.set_string("indexname", idxname)?; 52 | ts.set_string("tablename", tblname)?; 53 | ts.set_string("fieldname", fldname)?; 54 | Ok(()) 55 | } 56 | 57 | pub fn get_index_info( 58 | &self, 59 | tblname: &str, 60 | tx: Arc>, 61 | ) -> Result, TransactionError> { 62 | let mut result = HashMap::new(); 63 | let mut ts = TableScan::new(tx.clone(), "idxcat", self.layout.clone())?; 64 | while ts.next()? { 65 | if ts.get_string("tablename")? == tblname { 66 | let idxname = ts.get_string("indexname")?; 67 | let fldname = ts.get_string("fieldname")?; 68 | let tbl_layout = self.tblmgr.get_layout(tblname, tx.clone())?; 69 | let tblsi = self.statmgr.lock().unwrap().get_stat_info( 70 | tblname, 71 | tbl_layout.clone(), 72 | tx.clone(), 73 | )?; 74 | let ii = IndexInfo::new(&idxname, &fldname, tbl_layout.schema(), tx.clone(), tblsi); 75 | result.insert(fldname, ii); 76 | } 77 | } 78 | ts.close()?; 79 | Ok(result) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/metadata/metadatamgr.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | record::{layout::Layout, schema::Schema}, 8 | tx::transaction::{Transaction, TransactionError}, 9 | }; 10 | 11 | use super::{ 12 | indexinfo::IndexInfo, indexmgr::IndexMgr, statinfo::StatInfo, statmgr::StatMgr, 13 | tablemgr::TableMgr, viewmgr::ViewMgr, 14 | }; 15 | 16 | pub struct MetadataMgr { 17 | tblmgr: Arc, 18 | viewmgr: ViewMgr, 19 | statmgr: Arc>, 20 | idxmgr: IndexMgr, 21 | } 22 | 23 | impl MetadataMgr { 24 | pub fn new(isnew: bool, tx: Arc>) -> Result { 25 | let tblmgr = Arc::new(TableMgr::new(isnew, tx.clone())?); 26 | let viewmgr = ViewMgr::new(isnew, tblmgr.clone(), tx.clone())?; 27 | let statmgr = Arc::new(Mutex::new(StatMgr::new(tblmgr.clone(), tx.clone())?)); 28 | let idxmgr = IndexMgr::new(isnew, tblmgr.clone(), statmgr.clone(), tx)?; 29 | Ok(MetadataMgr { 30 | tblmgr, 31 | viewmgr, 32 | statmgr, 33 | idxmgr, 34 | }) 35 | } 36 | 37 | pub fn create_table( 38 | &self, 39 | tblname: &str, 40 | sch: Arc, 41 | tx: Arc>, 42 | ) -> Result<(), TransactionError> { 43 | self.tblmgr.create_table(tblname, sch, tx) 44 | } 45 | 46 | pub fn get_layout( 47 | &self, 48 | tblname: &str, 49 | tx: Arc>, 50 | ) -> Result { 51 | self.tblmgr.get_layout(tblname, tx) 52 | } 53 | 54 | pub fn create_view( 55 | &self, 56 | viewname: &str, 57 | viewdef: &str, 58 | tx: Arc>, 59 | ) -> Result<(), TransactionError> { 60 | self.viewmgr.create_view(viewname, viewdef, tx) 61 | } 62 | 63 | pub fn get_view_def( 64 | &self, 65 | viewname: &str, 66 | tx: Arc>, 67 | ) -> Result, TransactionError> { 68 | self.viewmgr.get_view_def(viewname, tx) 69 | } 70 | 71 | pub fn create_index( 72 | &self, 73 | idxname: &str, 74 | tblname: &str, 75 | fldname: &str, 76 | tx: Arc>, 77 | ) -> Result<(), TransactionError> { 78 | self.idxmgr.create_index(idxname, tblname, fldname, tx) 79 | } 80 | 81 | pub fn get_index_info( 82 | &self, 83 | tblname: &str, 84 | tx: Arc>, 85 | ) -> Result, TransactionError> { 86 | self.idxmgr.get_index_info(tblname, tx) 87 | } 88 | 89 | pub fn get_stat_info( 90 | &mut self, 91 | tblname: &str, 92 | layout: Layout, 93 | tx: Arc>, 94 | ) -> Result { 95 | self.statmgr 96 | .lock() 97 | .unwrap() 98 | .get_stat_info(tblname, layout, tx) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/metadata/statinfo.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub struct StatInfo { 3 | num_blocks: usize, 4 | num_recs: usize, 5 | } 6 | 7 | impl StatInfo { 8 | pub fn new(num_blocks: usize, num_recs: usize) -> StatInfo { 9 | StatInfo { 10 | num_blocks, 11 | num_recs, 12 | } 13 | } 14 | 15 | pub fn blocks_accessed(&self) -> usize { 16 | self.num_blocks 17 | } 18 | 19 | pub fn records_output(&self) -> usize { 20 | self.num_recs 21 | } 22 | 23 | pub fn distinct_values(&self, _fldname: &str) -> usize { 24 | 1 + (self.num_recs / 3) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/metadata/statmgr.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | query::{scan::ScanControl, updatescan::UpdateScanControl}, 8 | record::{layout::Layout, tablescan::TableScan}, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::{statinfo::StatInfo, tablemgr::TableMgr}; 13 | 14 | pub struct StatMgr { 15 | tbl_mgr: Arc, 16 | tablestats: HashMap, 17 | numcalls: usize, 18 | } 19 | 20 | impl StatMgr { 21 | pub fn new( 22 | tbl_mgr: Arc, 23 | tx: Arc>, 24 | ) -> Result { 25 | let tablestats = HashMap::new(); 26 | let numcalls = 0; 27 | let mut sm = StatMgr { 28 | tbl_mgr, 29 | tablestats, 30 | numcalls, 31 | }; 32 | sm.refresh_statistics(tx)?; 33 | Ok(sm) 34 | } 35 | 36 | pub fn get_stat_info( 37 | &mut self, 38 | tblname: &str, 39 | layout: Layout, 40 | tx: Arc>, 41 | ) -> Result { 42 | self.numcalls += 1; 43 | if self.numcalls > 100 { 44 | self.refresh_statistics(tx.clone())?; 45 | } 46 | let si = self.tablestats.get(tblname); 47 | if let Some(si) = si { 48 | return Ok(*si); 49 | } 50 | let si = self.calc_table_stats(tblname, layout, tx)?; 51 | self.tablestats.insert(tblname.to_string(), si); 52 | Ok(si) 53 | } 54 | 55 | fn refresh_statistics(&mut self, tx: Arc>) -> Result<(), TransactionError> { 56 | self.tablestats = HashMap::new(); 57 | self.numcalls = 0; 58 | let tcatlayout = self.tbl_mgr.get_layout("tblcat", tx.clone())?; 59 | let mut tcat = TableScan::new(tx.clone(), "tblcat", tcatlayout)?; 60 | while tcat.next()? { 61 | let tblname = tcat.get_string("tblname")?; 62 | let layout = self.tbl_mgr.get_layout(&tblname, tx.clone())?; 63 | let si = self.calc_table_stats(&tblname, layout, tx.clone())?; 64 | self.tablestats.insert(tblname, si); 65 | } 66 | tcat.close()?; 67 | Ok(()) 68 | } 69 | 70 | fn calc_table_stats( 71 | &self, 72 | tblname: &str, 73 | layout: Layout, 74 | tx: Arc>, 75 | ) -> Result { 76 | let mut num_recs = 0; 77 | let mut numblocks = 0; 78 | let mut ts = TableScan::new(tx, tblname, layout)?; 79 | while ts.next()? { 80 | num_recs += 1; 81 | if let Some(rid) = ts.get_rid() { 82 | numblocks = rid.block_number() as usize + 1; 83 | } 84 | } 85 | ts.close()?; 86 | Ok(StatInfo::new(numblocks, num_recs)) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/metadata/tablemgrtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | metadata::tablemgr::TableMgr, 10 | record::schema::{Schema, Type}, 11 | server::simpledb::SimpleDB, 12 | }; 13 | 14 | #[test] 15 | fn tablemgrtest() { 16 | let db = SimpleDB::with_params("tblmgrtest", 400, 8).unwrap(); 17 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 18 | let tm = TableMgr::new(true, tx.clone()).unwrap(); 19 | 20 | let mut sch = Schema::new(); 21 | sch.add_int_field("A"); 22 | sch.add_string_field("B", 9); 23 | tm.create_table("MyTable", Arc::new(sch), tx.clone()) 24 | .unwrap(); 25 | 26 | let layout = tm.get_layout("MyTable", tx.clone()).unwrap(); 27 | let size = layout.slot_size(); 28 | let sch2 = layout.schema(); 29 | assert_eq!(size, 21); 30 | for (i, fldname) in sch2.fields().iter().enumerate() { 31 | match sch2.type_(&fldname) { 32 | Type::Integer => assert_eq!(i, 0), 33 | Type::Varchar => { 34 | assert_eq!(i, 1); 35 | assert_eq!(sch2.length(&fldname), 9); 36 | } 37 | }; 38 | } 39 | tx.lock().unwrap().commit().unwrap(); 40 | 41 | fs::remove_dir_all("tblmgrtest").unwrap(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/metadata/viewmgr.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | query::{scan::ScanControl, updatescan::UpdateScanControl}, 5 | record::{schema::Schema, tablescan::TableScan}, 6 | tx::transaction::{Transaction, TransactionError}, 7 | }; 8 | 9 | use super::tablemgr::TableMgr; 10 | 11 | pub struct ViewMgr { 12 | tbl_mgr: Arc, 13 | } 14 | 15 | impl ViewMgr { 16 | const MAX_VIEWDEF: usize = 100; 17 | 18 | pub fn new( 19 | is_new: bool, 20 | tbl_mgr: Arc, 21 | tx: Arc>, 22 | ) -> Result { 23 | if is_new { 24 | let mut sch = Schema::new(); 25 | sch.add_string_field("viewname", TableMgr::MAX_NAME); 26 | sch.add_string_field("viewdef", ViewMgr::MAX_VIEWDEF); 27 | tbl_mgr.create_table("viewcat", Arc::new(sch), tx)?; 28 | } 29 | Ok(ViewMgr { tbl_mgr }) 30 | } 31 | 32 | pub fn create_view( 33 | &self, 34 | vname: &str, 35 | vdef: &str, 36 | tx: Arc>, 37 | ) -> Result<(), TransactionError> { 38 | let layout = self.tbl_mgr.get_layout("viewcat", tx.clone())?; 39 | let mut ts = TableScan::new(tx, "viewcat", layout)?; 40 | ts.insert()?; 41 | ts.set_string("viewname", vname)?; 42 | ts.set_string("viewdef", vdef)?; 43 | ts.close()?; 44 | Ok(()) 45 | } 46 | 47 | pub fn get_view_def( 48 | &self, 49 | vname: &str, 50 | tx: Arc>, 51 | ) -> Result, TransactionError> { 52 | let layout = self.tbl_mgr.get_layout("viewcat", tx.clone())?; 53 | let mut ts = TableScan::new(tx, "viewcat", layout)?; 54 | while ts.next()? { 55 | if ts.get_string("viewname")? == vname { 56 | let result = ts.get_string("viewdef")?; 57 | ts.close()?; 58 | return Ok(Some(result)); 59 | } 60 | } 61 | ts.close()?; 62 | Ok(None) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/multibuffer.rs: -------------------------------------------------------------------------------- 1 | pub mod bufferneeds; 2 | pub mod chunkscan; 3 | pub mod multibufferproductplan; 4 | pub mod multibufferproductscan; 5 | -------------------------------------------------------------------------------- /src/multibuffer/bufferneeds.rs: -------------------------------------------------------------------------------- 1 | pub struct BufferNeeds {} 2 | 3 | impl BufferNeeds { 4 | pub fn best_factor(available: usize, size: usize) -> usize { 5 | let avail = available - 2; 6 | if avail <= 1 { 7 | return 1; 8 | } 9 | let mut k = size; 10 | let mut i = 1.0; 11 | while k > avail { 12 | i += 1.0; 13 | k = (size as f64 / i).ceil() as usize; 14 | } 15 | k 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/multibuffer/multibufferproductplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | materialize::{materializeplan::MaterializePlan, temptable::TempTable}, 5 | plan::plan::{Plan, PlanControl}, 6 | query::{ 7 | scan::{Scan, ScanControl}, 8 | updatescan::UpdateScanControl, 9 | }, 10 | record::schema::Schema, 11 | tx::transaction::{Transaction, TransactionError}, 12 | }; 13 | 14 | use super::multibufferproductscan::MultibufferProductScan; 15 | 16 | #[derive(Clone)] 17 | pub struct MultibufferProductPlan { 18 | tx: Arc>, 19 | lhs: Box, 20 | rhs: Box, 21 | schema: Arc, 22 | } 23 | 24 | impl MultibufferProductPlan { 25 | pub fn new(tx: Arc>, lhs: Plan, rhs: Plan) -> MultibufferProductPlan { 26 | let lhs: Plan = MaterializePlan::new(tx.clone(), lhs).into(); 27 | let mut s = Schema::new(); 28 | s.add_all(&lhs.schema()); 29 | s.add_all(&rhs.schema()); 30 | let schema = Arc::new(s); 31 | MultibufferProductPlan { 32 | tx, 33 | lhs: Box::new(lhs), 34 | rhs: Box::new(rhs), 35 | schema, 36 | } 37 | } 38 | 39 | fn copy_records_from(&self, p: &Plan) -> Result { 40 | let mut src = p.open()?; 41 | let sch = p.schema(); 42 | let t = TempTable::new(self.tx.clone(), sch.clone()); 43 | let mut dest = t.open()?; 44 | while src.next()? { 45 | dest.insert()?; 46 | for fldname in sch.fields() { 47 | dest.set_val(fldname, src.get_val(fldname)?)?; 48 | } 49 | } 50 | src.close()?; 51 | dest.close()?; 52 | Ok(t) 53 | } 54 | } 55 | 56 | impl PlanControl for MultibufferProductPlan { 57 | fn open(&self) -> Result { 58 | let leftscan = self.lhs.open()?; 59 | let tt = self.copy_records_from(&self.rhs)?; 60 | Ok(MultibufferProductScan::new( 61 | self.tx.clone(), 62 | leftscan, 63 | &tt.table_name(), 64 | tt.get_layout(), 65 | )? 66 | .into()) 67 | } 68 | 69 | fn blocks_accessed(&self) -> usize { 70 | let avail = self.tx.lock().unwrap().available_buffs(); 71 | let size = MaterializePlan::new(self.tx.clone(), *self.rhs.clone()).blocks_accessed(); 72 | let numchunks = size / avail; 73 | self.rhs.blocks_accessed() + (self.lhs.blocks_accessed() * numchunks) 74 | } 75 | 76 | fn records_output(&self) -> usize { 77 | self.lhs.records_output() * self.rhs.records_output() 78 | } 79 | 80 | fn distinct_values(&self, fldname: &str) -> usize { 81 | if self.lhs.schema().has_field(fldname) { 82 | return self.lhs.distinct_values(fldname); 83 | } 84 | self.rhs.distinct_values(fldname) 85 | } 86 | 87 | fn schema(&self) -> Arc { 88 | self.schema.clone() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/opt.rs: -------------------------------------------------------------------------------- 1 | pub mod heuristicqueryplanner; 2 | pub mod tableplanner; 3 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | pub mod badsyntaxerror; 2 | pub mod createindexdata; 3 | pub mod createtabledata; 4 | pub mod createviewdata; 5 | pub mod deletedata; 6 | pub mod insertdata; 7 | pub mod lexer; 8 | pub mod lexertest; 9 | pub mod modifydata; 10 | pub mod parser; 11 | pub mod parsertest; 12 | pub mod querydata; 13 | -------------------------------------------------------------------------------- /src/parse/badsyntaxerror.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct BadSyntaxError; 3 | -------------------------------------------------------------------------------- /src/parse/createindexdata.rs: -------------------------------------------------------------------------------- 1 | use super::parser::ObjectControl; 2 | 3 | pub struct CreateIndexData { 4 | idxname: String, 5 | tblname: String, 6 | fldname: String, 7 | } 8 | 9 | impl CreateIndexData { 10 | pub fn new(idxname: &str, tblname: &str, fldname: &str) -> CreateIndexData { 11 | CreateIndexData { 12 | idxname: idxname.to_string(), 13 | tblname: tblname.to_string(), 14 | fldname: fldname.to_string(), 15 | } 16 | } 17 | 18 | pub fn index_name(&self) -> String { 19 | self.idxname.clone() 20 | } 21 | 22 | pub fn table_name(&self) -> String { 23 | self.tblname.clone() 24 | } 25 | 26 | pub fn field_name(&self) -> String { 27 | self.fldname.clone() 28 | } 29 | } 30 | 31 | impl ObjectControl for CreateIndexData {} 32 | -------------------------------------------------------------------------------- /src/parse/createtabledata.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::record::schema::Schema; 4 | 5 | use super::parser::ObjectControl; 6 | 7 | pub struct CreateTableData { 8 | tblname: String, 9 | sch: Arc, 10 | } 11 | 12 | impl CreateTableData { 13 | pub fn new(tblname: &str, sch: Schema) -> CreateTableData { 14 | CreateTableData { 15 | tblname: tblname.to_string(), 16 | sch: Arc::new(sch), 17 | } 18 | } 19 | 20 | pub fn table_name(&self) -> String { 21 | self.tblname.clone() 22 | } 23 | 24 | pub fn new_schema(&self) -> Arc { 25 | self.sch.clone() 26 | } 27 | } 28 | 29 | impl ObjectControl for CreateTableData {} 30 | -------------------------------------------------------------------------------- /src/parse/createviewdata.rs: -------------------------------------------------------------------------------- 1 | use super::{parser::ObjectControl, querydata::QueryData}; 2 | 3 | pub struct CreateViewData { 4 | viewname: String, 5 | qry_data: QueryData, 6 | } 7 | 8 | impl CreateViewData { 9 | pub fn new(viewname: &str, qry_data: QueryData) -> CreateViewData { 10 | CreateViewData { 11 | viewname: viewname.to_string(), 12 | qry_data, 13 | } 14 | } 15 | 16 | pub fn view_name(&self) -> String { 17 | self.viewname.clone() 18 | } 19 | 20 | pub fn view_def(&self) -> String { 21 | format!("{}", self.qry_data) 22 | } 23 | } 24 | 25 | impl ObjectControl for CreateViewData {} 26 | -------------------------------------------------------------------------------- /src/parse/deletedata.rs: -------------------------------------------------------------------------------- 1 | use crate::query::predicate::Predicate; 2 | 3 | use super::parser::ObjectControl; 4 | 5 | pub struct DeleteData { 6 | tblname: String, 7 | pred: Predicate, 8 | } 9 | 10 | impl DeleteData { 11 | pub fn new(tblname: &str, pred: Predicate) -> DeleteData { 12 | DeleteData { 13 | tblname: tblname.to_string(), 14 | pred, 15 | } 16 | } 17 | 18 | pub fn table_name(&self) -> String { 19 | self.tblname.clone() 20 | } 21 | 22 | pub fn pred(&self) -> Predicate { 23 | self.pred.clone() 24 | } 25 | } 26 | 27 | impl ObjectControl for DeleteData {} 28 | -------------------------------------------------------------------------------- /src/parse/insertdata.rs: -------------------------------------------------------------------------------- 1 | use crate::query::constant::Constant; 2 | 3 | use super::parser::ObjectControl; 4 | 5 | pub struct InsertData { 6 | tblname: String, 7 | flds: Vec, 8 | vals: Vec, 9 | } 10 | 11 | impl InsertData { 12 | pub fn new(tblname: &str, flds: Vec, vals: Vec) -> InsertData { 13 | InsertData { 14 | tblname: tblname.to_string(), 15 | flds, 16 | vals, 17 | } 18 | } 19 | 20 | pub fn table_name(&self) -> String { 21 | self.tblname.clone() 22 | } 23 | 24 | pub fn fields(&self) -> Vec { 25 | self.flds.clone() 26 | } 27 | 28 | pub fn vals(&self) -> Vec { 29 | self.vals.clone() 30 | } 31 | } 32 | 33 | impl ObjectControl for InsertData {} 34 | -------------------------------------------------------------------------------- /src/parse/lexertest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::parse::lexer::Lexer; 4 | 5 | #[test] 6 | fn lexertest() { 7 | let ss = [ 8 | "a=1", "1=a", "z=1", "1=z", "_=1", "1=_", "A=1", "1=A", "a=-1", "-1=a", " a = 1 ", 9 | " 1 = a ", "ab=12", "12=ab", 10 | ]; 11 | let e = [ 12 | ("a", 1), 13 | ("a", 1), 14 | ("z", 1), 15 | ("z", 1), 16 | ("_", 1), 17 | ("_", 1), 18 | ("a", 1), 19 | ("a", 1), 20 | ("a", -1), 21 | ("a", -1), 22 | ("a", 1), 23 | ("a", 1), 24 | ("ab", 12), 25 | ("ab", 12), 26 | ]; 27 | for (i, s) in ss.iter().enumerate() { 28 | let mut lex = Lexer::new(&s); 29 | let x; 30 | let y; 31 | if lex.match_id() { 32 | x = lex.eat_id().unwrap(); 33 | lex.eat_delim('=').unwrap(); 34 | y = lex.eat_int_constant().unwrap(); 35 | } else { 36 | y = lex.eat_int_constant().unwrap(); 37 | lex.eat_delim('=').unwrap(); 38 | x = lex.eat_id().unwrap(); 39 | } 40 | assert_eq!(x, e[i].0); 41 | assert_eq!(y, e[i].1); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/parse/modifydata.rs: -------------------------------------------------------------------------------- 1 | use crate::query::{expression::Expression, predicate::Predicate}; 2 | 3 | use super::parser::ObjectControl; 4 | 5 | pub struct ModifyData { 6 | tblname: String, 7 | fldname: String, 8 | newval: Expression, 9 | pred: Predicate, 10 | } 11 | 12 | impl ModifyData { 13 | pub fn new(tblname: &str, fldname: &str, newval: Expression, pred: Predicate) -> ModifyData { 14 | ModifyData { 15 | tblname: tblname.to_string(), 16 | fldname: fldname.to_string(), 17 | newval, 18 | pred, 19 | } 20 | } 21 | 22 | pub fn table_name(&self) -> String { 23 | self.tblname.clone() 24 | } 25 | 26 | pub fn target_field(&self) -> String { 27 | self.fldname.clone() 28 | } 29 | 30 | pub fn new_value(&self) -> Expression { 31 | self.newval.clone() 32 | } 33 | 34 | pub fn pred(&self) -> Predicate { 35 | self.pred.clone() 36 | } 37 | } 38 | 39 | impl ObjectControl for ModifyData {} 40 | -------------------------------------------------------------------------------- /src/parse/parsertest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::parse::parser::Parser; 4 | 5 | #[test] 6 | fn parsertest() { 7 | let ss = [ 8 | ("select a from x where b = 3", true), 9 | ("select a, b from x,y,z", true), 10 | ("delete from x where a = b and c = 0", true), 11 | ("update x set a = b where c = 3", true), 12 | ("insert into x (a, b, c) values (3, 'glop', 4)", true), 13 | ("create table x ( a varchar(3), b int, c varchar(2) )", true), 14 | ("select from x", false), 15 | ("select x x from x", false), 16 | ("select a from where b=3", false), 17 | ("select a from y where b -=3", false), 18 | ("select a from y where", false), 19 | ]; 20 | for (s, b) in ss.iter() { 21 | let mut p = Parser::new(&s.trim_end()); 22 | let ok; 23 | if s.starts_with("select") { 24 | ok = p.query().is_ok(); 25 | } else { 26 | ok = p.update_cmd().is_ok(); 27 | } 28 | assert_eq!(&ok, b); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/parse/querydata.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::query::predicate::Predicate; 4 | 5 | pub struct QueryData { 6 | fields: Vec, 7 | tables: Vec, 8 | pred: Predicate, 9 | } 10 | 11 | impl fmt::Display for QueryData { 12 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 13 | let mut predstring = format!("{}", self.pred); 14 | if !predstring.is_empty() { 15 | predstring = format!("where {}", predstring); 16 | } 17 | 18 | write!( 19 | f, 20 | "select {} from {}{}", 21 | self.fields.join(", "), 22 | self.tables.join(", "), 23 | predstring, 24 | ) 25 | } 26 | } 27 | 28 | impl QueryData { 29 | pub fn new(fields: Vec, tables: Vec, pred: Predicate) -> QueryData { 30 | QueryData { 31 | fields, 32 | tables, 33 | pred, 34 | } 35 | } 36 | 37 | pub fn fields(&self) -> Vec { 38 | self.fields.clone() 39 | } 40 | 41 | pub fn tables(&self) -> Vec { 42 | self.tables.clone() 43 | } 44 | 45 | pub fn pred(&self) -> Predicate { 46 | self.pred.clone() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/plan.rs: -------------------------------------------------------------------------------- 1 | pub mod basicqueryplanner; 2 | pub mod basicupdateplanner; 3 | pub mod betterqueryplanner; 4 | pub mod multitableplantest; 5 | pub mod optimizedproductplan; 6 | pub mod plan; 7 | pub mod planner; 8 | pub mod plannerstudenttest; 9 | pub mod plannertest1; 10 | pub mod plannertest2; 11 | pub mod productplan; 12 | pub mod projectplan; 13 | pub mod queryplanner; 14 | pub mod selectplan; 15 | pub mod singletableplantest; 16 | pub mod tableplan; 17 | pub mod updateplanner; 18 | -------------------------------------------------------------------------------- /src/plan/basicqueryplanner.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | metadata::metadatamgr::MetadataMgr, 5 | parse::{parser::Parser, querydata::QueryData}, 6 | tx::transaction::Transaction, 7 | }; 8 | 9 | use super::{ 10 | plan::{Plan, PlanError}, 11 | productplan::ProductPlan, 12 | projectplan::ProjectPlan, 13 | queryplanner::QueryPlannerControl, 14 | selectplan::SelectPlan, 15 | tableplan::TablePlan, 16 | }; 17 | 18 | pub struct BasicQueryPlanner { 19 | mdm: Arc>, 20 | } 21 | 22 | impl BasicQueryPlanner { 23 | #[allow(dead_code)] 24 | pub fn new(mdm: Arc>) -> BasicQueryPlanner { 25 | BasicQueryPlanner { mdm } 26 | } 27 | } 28 | 29 | impl QueryPlannerControl for BasicQueryPlanner { 30 | fn create_plan( 31 | &mut self, 32 | data: QueryData, 33 | tx: Arc>, 34 | ) -> Result { 35 | let mut plans = Vec::new(); 36 | for tblname in data.tables() { 37 | let viewdef = self 38 | .mdm 39 | .lock() 40 | .unwrap() 41 | .get_view_def(&tblname, tx.clone())?; 42 | if let Some(viewdef) = viewdef { 43 | let mut parser = Parser::new(&viewdef); 44 | let viewdata = parser.query()?; 45 | plans.push(self.create_plan(viewdata, tx.clone())?); 46 | } else { 47 | plans.push(TablePlan::new(tx.clone(), &tblname, self.mdm.clone())?.into()); 48 | } 49 | } 50 | 51 | let mut p = plans.remove(0); 52 | for nextplan in plans { 53 | p = ProductPlan::new(p, nextplan).into(); 54 | } 55 | 56 | p = SelectPlan::new(p, data.pred()).into(); 57 | 58 | Ok(ProjectPlan::new(p, data.fields()).into()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/plan/betterqueryplanner.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | metadata::metadatamgr::MetadataMgr, 5 | parse::{parser::Parser, querydata::QueryData}, 6 | tx::transaction::Transaction, 7 | }; 8 | 9 | use super::{ 10 | plan::{Plan, PlanControl, PlanError}, 11 | productplan::ProductPlan, 12 | projectplan::ProjectPlan, 13 | queryplanner::QueryPlannerControl, 14 | selectplan::SelectPlan, 15 | tableplan::TablePlan, 16 | }; 17 | 18 | pub struct BetterQueryPlanner { 19 | mdm: Arc>, 20 | } 21 | 22 | impl BetterQueryPlanner { 23 | #[allow(dead_code)] 24 | pub fn new(mdm: Arc>) -> BetterQueryPlanner { 25 | BetterQueryPlanner { mdm } 26 | } 27 | } 28 | 29 | impl QueryPlannerControl for BetterQueryPlanner { 30 | fn create_plan( 31 | &mut self, 32 | data: QueryData, 33 | tx: Arc>, 34 | ) -> Result { 35 | let mut plans = Vec::new(); 36 | for tblname in data.tables() { 37 | let viewdef = self 38 | .mdm 39 | .lock() 40 | .unwrap() 41 | .get_view_def(&tblname, tx.clone())?; 42 | if let Some(viewdef) = viewdef { 43 | let mut parser = Parser::new(&viewdef); 44 | let viewdata = parser.query()?; 45 | plans.push(self.create_plan(viewdata, tx.clone())?); 46 | } else { 47 | plans.push(TablePlan::new(tx.clone(), &tblname, self.mdm.clone())?.into()); 48 | } 49 | } 50 | 51 | let mut p = plans.remove(0); 52 | for nextplan in plans { 53 | let choice1: Plan = ProductPlan::new(nextplan.clone(), p.clone()).into(); 54 | let choice2: Plan = ProductPlan::new(p.clone(), nextplan).into(); 55 | if choice1.blocks_accessed() < choice2.blocks_accessed() { 56 | p = choice1; 57 | } else { 58 | p = choice2; 59 | } 60 | } 61 | 62 | p = SelectPlan::new(p, data.pred()).into(); 63 | 64 | Ok(ProjectPlan::new(p, data.fields()).into()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/plan/optimizedproductplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{query::scan::Scan, record::schema::Schema, tx::transaction::TransactionError}; 4 | 5 | use super::{ 6 | plan::{Plan, PlanControl}, 7 | productplan::ProductPlan, 8 | }; 9 | 10 | #[derive(Clone)] 11 | pub struct OptimizedProductPlan { 12 | bestplan: Box, 13 | } 14 | 15 | impl OptimizedProductPlan { 16 | #[allow(dead_code)] 17 | pub fn new(p1: Plan, p2: Plan) -> OptimizedProductPlan { 18 | let prod1 = ProductPlan::new(p1.clone(), p2.clone()); 19 | let prod2 = ProductPlan::new(p2, p1); 20 | let b1 = prod1.blocks_accessed(); 21 | let b2 = prod2.blocks_accessed(); 22 | let bestplan = Box::new(if b1 < b2 { prod1 } else { prod2 }.into()); 23 | OptimizedProductPlan { bestplan } 24 | } 25 | } 26 | 27 | impl PlanControl for OptimizedProductPlan { 28 | fn open(&self) -> Result { 29 | self.bestplan.open() 30 | } 31 | 32 | fn blocks_accessed(&self) -> usize { 33 | self.bestplan.blocks_accessed() 34 | } 35 | 36 | fn records_output(&self) -> usize { 37 | self.bestplan.records_output() 38 | } 39 | 40 | fn distinct_values(&self, fldname: &str) -> usize { 41 | self.bestplan.distinct_values(fldname) 42 | } 43 | 44 | fn schema(&self) -> Arc { 45 | self.bestplan.schema() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/plan/plan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use enum_dispatch::enum_dispatch; 4 | 5 | use crate::{ 6 | index::planner::{indexjoinplan::IndexJoinPlan, indexselectplan::IndexSelectPlan}, 7 | materialize::{ 8 | groupbyplan::GroupByPlan, materializeplan::MaterializePlan, mergejoinplan::MergeJoinPlan, 9 | sortplan::SortPlan, 10 | }, 11 | multibuffer::multibufferproductplan::MultibufferProductPlan, 12 | parse::badsyntaxerror::BadSyntaxError, 13 | query::scan::Scan, 14 | record::schema::Schema, 15 | tx::transaction::TransactionError, 16 | }; 17 | 18 | use super::{ 19 | optimizedproductplan::OptimizedProductPlan, productplan::ProductPlan, projectplan::ProjectPlan, 20 | selectplan::SelectPlan, tableplan::TablePlan, 21 | }; 22 | 23 | #[derive(Debug)] 24 | pub enum PlanError { 25 | Transaction(TransactionError), 26 | Syntax(BadSyntaxError), 27 | General, 28 | } 29 | 30 | impl From for PlanError { 31 | fn from(e: TransactionError) -> Self { 32 | PlanError::Transaction(e) 33 | } 34 | } 35 | 36 | impl From for PlanError { 37 | fn from(e: BadSyntaxError) -> Self { 38 | PlanError::Syntax(e) 39 | } 40 | } 41 | 42 | #[enum_dispatch(Plan)] 43 | pub trait PlanControl { 44 | fn open(&self) -> Result; 45 | fn blocks_accessed(&self) -> usize; 46 | fn records_output(&self) -> usize; 47 | fn distinct_values(&self, fldname: &str) -> usize; 48 | fn schema(&self) -> Arc; 49 | } 50 | 51 | #[derive(Clone)] 52 | #[enum_dispatch] 53 | pub enum Plan { 54 | Table(TablePlan), 55 | Select(SelectPlan), 56 | Project(ProjectPlan), 57 | Product(ProductPlan), 58 | IndexSelect(IndexSelectPlan), 59 | IndexJoin(IndexJoinPlan), 60 | Materialize(MaterializePlan), 61 | MultibufferProduct(MultibufferProductPlan), 62 | Sort(SortPlan), 63 | GroupBy(GroupByPlan), 64 | MergeJoin(MergeJoinPlan), 65 | OptimizedProduct(OptimizedProductPlan), 66 | } 67 | -------------------------------------------------------------------------------- /src/plan/planner.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | parse::{ 5 | parser::{Object, Parser}, 6 | querydata::QueryData, 7 | }, 8 | tx::transaction::Transaction, 9 | }; 10 | 11 | use super::{ 12 | plan::{Plan, PlanError}, 13 | queryplanner::{QueryPlanner, QueryPlannerControl}, 14 | updateplanner::{UpdatePlanner, UpdatePlannerControl}, 15 | }; 16 | 17 | pub struct Planner { 18 | qplanner: QueryPlanner, 19 | uplanner: UpdatePlanner, 20 | } 21 | 22 | impl Planner { 23 | pub fn new(qplanner: QueryPlanner, uplanner: UpdatePlanner) -> Planner { 24 | Planner { qplanner, uplanner } 25 | } 26 | 27 | pub fn create_query_plan( 28 | &mut self, 29 | qry: &str, 30 | tx: Arc>, 31 | ) -> Result { 32 | let mut parser = Parser::new(qry); 33 | let data = parser.query()?; 34 | self.verify_query(&data); 35 | self.qplanner.create_plan(data, tx) 36 | } 37 | 38 | pub fn execute_update( 39 | &self, 40 | cmd: &str, 41 | tx: Arc>, 42 | ) -> Result { 43 | let mut parser = Parser::new(cmd); 44 | let data = parser.update_cmd()?; 45 | self.verify_update(&data); 46 | Ok(match data { 47 | Object::Insert(object) => self.uplanner.execute_insert(&object, tx)?, 48 | Object::Delete(object) => self.uplanner.execute_delete(&object, tx)?, 49 | Object::Modify(object) => self.uplanner.execute_modify(&object, tx)?, 50 | Object::CreateTable(object) => self.uplanner.execute_create_table(&object, tx)?, 51 | Object::CreateView(object) => self.uplanner.execute_create_view(&object, tx)?, 52 | Object::CreateIndex(object) => self.uplanner.execute_create_index(&object, tx)?, 53 | }) 54 | } 55 | 56 | fn verify_query(&self, _: &QueryData) {} 57 | 58 | fn verify_update(&self, _: &Object) {} 59 | } 60 | -------------------------------------------------------------------------------- /src/plan/plannerstudenttest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | api::{ 10 | connection::ConnectionControl, driver::DriverControl, 11 | embedded::embeddeddriver::EmbeddedDriver, statement::StatementControl, 12 | }, 13 | plan::plan::PlanControl, 14 | query::scan::ScanControl, 15 | server::simpledb::SimpleDB, 16 | }; 17 | 18 | #[test] 19 | fn plannerstudenttest() { 20 | create_student_db(); 21 | 22 | let db = SimpleDB::new("plannerstudenttest").unwrap(); 23 | let planner = db.planner().unwrap(); 24 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 25 | 26 | let qry = "select sname, gradyear from student"; 27 | let p = planner 28 | .lock() 29 | .unwrap() 30 | .create_query_plan(qry, tx.clone()) 31 | .unwrap(); 32 | let mut s = p.open().unwrap(); 33 | let mut i = 0; 34 | let snames = [ 35 | "joe", "amy", "max", "sue", "bob", "kim", "art", "pat", "lee", 36 | ]; 37 | let gradyears = [2021, 2020, 2022, 2022, 2020, 2020, 2021, 2019, 2021]; 38 | while s.next().unwrap() { 39 | assert_eq!(snames[i], s.get_string("sname").unwrap()); 40 | assert_eq!(gradyears[i], s.get_int("gradyear").unwrap()); 41 | i += 1; 42 | } 43 | assert_eq!(i, snames.len()); 44 | assert_eq!(i, gradyears.len()); 45 | s.close().unwrap(); 46 | 47 | let cmd = "delete from STUDENT where MajorId = 30"; 48 | assert_eq!( 49 | planner 50 | .lock() 51 | .unwrap() 52 | .execute_update(cmd, tx.clone()) 53 | .unwrap(), 54 | 2 55 | ); 56 | 57 | tx.lock().unwrap().commit().unwrap(); 58 | 59 | fs::remove_dir_all("plannerstudenttest").unwrap(); 60 | } 61 | 62 | fn create_student_db() { 63 | let d = EmbeddedDriver::new(); 64 | let mut conn = d.connect("plannerstudenttest").unwrap(); 65 | let mut stmt = conn.create_statement(); 66 | 67 | let s = "create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int)"; 68 | stmt.execute_update(s).unwrap(); 69 | 70 | let s = "insert into STUDENT(SId, SName, MajorId, GradYear) values "; 71 | let studvals = [ 72 | "(1, 'joe', 10, 2021)", 73 | "(2, 'amy', 20, 2020)", 74 | "(3, 'max', 10, 2022)", 75 | "(4, 'sue', 20, 2022)", 76 | "(5, 'bob', 30, 2020)", 77 | "(6, 'kim', 20, 2020)", 78 | "(7, 'art', 30, 2021)", 79 | "(8, 'pat', 20, 2019)", 80 | "(9, 'lee', 10, 2021)", 81 | ]; 82 | for studval in studvals { 83 | stmt.execute_update(&format!("{}{}", s, studval)).unwrap(); 84 | } 85 | 86 | conn.close().unwrap(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/plan/plannertest1.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use rand::{distributions::Uniform, prelude::Distribution}; 9 | 10 | use crate::{plan::plan::PlanControl, query::scan::ScanControl, server::simpledb::SimpleDB}; 11 | 12 | #[test] 13 | fn plannertest1() { 14 | let db = SimpleDB::new("plannertest1").unwrap(); 15 | let planner = db.planner().unwrap(); 16 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 17 | let cmd = "create table T1(A int, B varchar(9))"; 18 | planner 19 | .lock() 20 | .unwrap() 21 | .execute_update(cmd, tx.clone()) 22 | .unwrap(); 23 | 24 | let n = 200; 25 | let mut rng = rand::thread_rng(); 26 | let die = Uniform::from(0..50); 27 | for _ in 0..n { 28 | let a = die.sample(&mut rng); 29 | let cmd = format!("insert into T1(A,B) values({0}, 'rec{0}')", a); 30 | planner 31 | .lock() 32 | .unwrap() 33 | .execute_update(&cmd, tx.clone()) 34 | .unwrap(); 35 | } 36 | 37 | let qry = "select B from T1 where A=10"; 38 | let p = planner 39 | .lock() 40 | .unwrap() 41 | .create_query_plan(qry, tx.clone()) 42 | .unwrap(); 43 | let mut s = p.open().unwrap(); 44 | while s.next().unwrap() { 45 | assert_eq!(s.get_string("b").unwrap(), "rec10"); 46 | } 47 | s.close().unwrap(); 48 | tx.lock().unwrap().commit().unwrap(); 49 | 50 | fs::remove_dir_all("plannertest1").unwrap(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/plan/plannertest2.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | collections::HashSet, 5 | fs, 6 | sync::{Arc, Mutex}, 7 | }; 8 | 9 | use crate::{plan::plan::PlanControl, query::scan::ScanControl, server::simpledb::SimpleDB}; 10 | 11 | #[test] 12 | fn plannertest2() { 13 | let db = SimpleDB::new("plannertest2").unwrap(); 14 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 15 | let planner = db.planner().unwrap(); 16 | 17 | let mut cmd = "create table T1(A int, B varchar(9))"; 18 | planner 19 | .lock() 20 | .unwrap() 21 | .execute_update(cmd, tx.clone()) 22 | .unwrap(); 23 | let n = 200; 24 | for i in 0..n { 25 | let a = i; 26 | let cmd = format!("insert into T1(A,B) values({0}, 'bbb{0}')", a); 27 | planner 28 | .lock() 29 | .unwrap() 30 | .execute_update(&cmd, tx.clone()) 31 | .unwrap(); 32 | } 33 | 34 | cmd = "create table T2(C int, D varchar(9))"; 35 | planner 36 | .lock() 37 | .unwrap() 38 | .execute_update(cmd, tx.clone()) 39 | .unwrap(); 40 | for i in 0..n { 41 | let c = n - i - 1; 42 | let cmd = format!("insert into T2(C,D) values ({0}, 'ddd{0}')", c); 43 | planner 44 | .lock() 45 | .unwrap() 46 | .execute_update(&cmd, tx.clone()) 47 | .unwrap(); 48 | } 49 | 50 | let qry = "select B,D from T1,T2 where A=C"; 51 | let p = planner 52 | .lock() 53 | .unwrap() 54 | .create_query_plan(qry, tx.clone()) 55 | .unwrap(); 56 | let mut s = p.open().unwrap(); 57 | let mut bs: HashSet = HashSet::from_iter((0..n).map(|x| format!("bbb{}", x))); 58 | let mut ds: HashSet = HashSet::from_iter((0..n).map(|x| format!("ddd{}", x))); 59 | while s.next().unwrap() { 60 | let b = s.get_string("b").unwrap(); 61 | assert!(bs.contains(&b)); 62 | bs.remove(&b); 63 | 64 | let d = s.get_string("d").unwrap(); 65 | assert!(ds.contains(&d)); 66 | ds.remove(&d); 67 | } 68 | assert!(bs.is_empty()); 69 | assert!(ds.is_empty()); 70 | s.close().unwrap(); 71 | tx.lock().unwrap().commit().unwrap(); 72 | 73 | fs::remove_dir_all("plannertest2").unwrap(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/plan/productplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | query::{productscan::ProductScan, scan::Scan}, 5 | record::schema::Schema, 6 | tx::transaction::TransactionError, 7 | }; 8 | 9 | use super::plan::{Plan, PlanControl}; 10 | 11 | #[derive(Clone)] 12 | pub struct ProductPlan { 13 | p1: Box, 14 | p2: Box, 15 | schema: Arc, 16 | } 17 | 18 | impl ProductPlan { 19 | pub fn new(p1: Plan, p2: Plan) -> ProductPlan { 20 | let mut schema = Schema::new(); 21 | schema.add_all(&p1.schema()); 22 | schema.add_all(&p2.schema()); 23 | ProductPlan { 24 | p1: Box::new(p1), 25 | p2: Box::new(p2), 26 | schema: Arc::new(schema), 27 | } 28 | } 29 | } 30 | 31 | impl PlanControl for ProductPlan { 32 | fn open(&self) -> Result { 33 | let s1 = self.p1.open()?; 34 | let s2 = self.p2.open()?; 35 | Ok(ProductScan::new(Arc::new(Mutex::new(s1)), Arc::new(Mutex::new(s2)))?.into()) 36 | } 37 | 38 | fn blocks_accessed(&self) -> usize { 39 | self.p1.blocks_accessed() + (self.p1.records_output() * self.p2.blocks_accessed()) 40 | } 41 | 42 | fn records_output(&self) -> usize { 43 | self.p1.records_output() * self.p2.records_output() 44 | } 45 | 46 | fn distinct_values(&self, fldname: &str) -> usize { 47 | if self.p1.schema().has_field(fldname) { 48 | return self.p1.distinct_values(fldname); 49 | } 50 | self.p2.distinct_values(fldname) 51 | } 52 | 53 | fn schema(&self) -> Arc { 54 | self.schema.clone() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/plan/projectplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{ 4 | query::{projectscan::ProjectScan, scan::Scan}, 5 | record::schema::Schema, 6 | tx::transaction::TransactionError, 7 | }; 8 | 9 | use super::plan::{Plan, PlanControl}; 10 | 11 | #[derive(Clone)] 12 | pub struct ProjectPlan { 13 | p: Box, 14 | schema: Arc, 15 | } 16 | 17 | impl ProjectPlan { 18 | pub fn new(p: Plan, field_list: Vec) -> ProjectPlan { 19 | let mut schema = Schema::new(); 20 | for fldname in field_list { 21 | schema.add(&fldname, &p.schema()); 22 | } 23 | ProjectPlan { 24 | p: Box::new(p), 25 | schema: Arc::new(schema), 26 | } 27 | } 28 | } 29 | 30 | impl PlanControl for ProjectPlan { 31 | fn open(&self) -> Result { 32 | let s = self.p.open()?; 33 | Ok(ProjectScan::new(s, self.schema.fields().clone()).into()) 34 | } 35 | 36 | fn blocks_accessed(&self) -> usize { 37 | self.p.blocks_accessed() 38 | } 39 | 40 | fn records_output(&self) -> usize { 41 | self.p.records_output() 42 | } 43 | 44 | fn distinct_values(&self, fldname: &str) -> usize { 45 | self.p.distinct_values(fldname) 46 | } 47 | 48 | fn schema(&self) -> Arc { 49 | self.schema.clone() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/plan/queryplanner.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use enum_dispatch::enum_dispatch; 4 | 5 | use crate::{ 6 | opt::heuristicqueryplanner::HeuristicQueryPlanner, parse::querydata::QueryData, 7 | tx::transaction::Transaction, 8 | }; 9 | 10 | use super::{ 11 | basicqueryplanner::BasicQueryPlanner, 12 | betterqueryplanner::BetterQueryPlanner, 13 | plan::{Plan, PlanError}, 14 | }; 15 | 16 | #[enum_dispatch(QueryPlanner)] 17 | pub trait QueryPlannerControl { 18 | fn create_plan( 19 | &mut self, 20 | data: QueryData, 21 | tx: Arc>, 22 | ) -> Result; 23 | } 24 | 25 | #[enum_dispatch] 26 | pub enum QueryPlanner { 27 | Basic(BasicQueryPlanner), 28 | Better(BetterQueryPlanner), 29 | Heuristic(HeuristicQueryPlanner), 30 | } 31 | -------------------------------------------------------------------------------- /src/plan/selectplan.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp, sync::Arc}; 2 | 3 | use crate::{ 4 | query::{predicate::Predicate, scan::Scan, selectscan::SelectScan}, 5 | record::schema::Schema, 6 | tx::transaction::TransactionError, 7 | }; 8 | 9 | use super::plan::{Plan, PlanControl}; 10 | 11 | #[derive(Clone)] 12 | pub struct SelectPlan { 13 | p: Box, 14 | pred: Predicate, 15 | } 16 | 17 | impl SelectPlan { 18 | pub fn new(p: Plan, pred: Predicate) -> SelectPlan { 19 | SelectPlan { 20 | p: Box::new(p), 21 | pred, 22 | } 23 | } 24 | } 25 | 26 | impl PlanControl for SelectPlan { 27 | fn open(&self) -> Result { 28 | let s = self.p.open()?; 29 | Ok(SelectScan::new(s, self.pred.clone()).into()) 30 | } 31 | 32 | fn blocks_accessed(&self) -> usize { 33 | self.p.blocks_accessed() 34 | } 35 | 36 | fn records_output(&self) -> usize { 37 | self.p.records_output() / self.pred.reduction_factor(&self.p) 38 | } 39 | 40 | fn distinct_values(&self, fldname: &str) -> usize { 41 | if self.pred.equates_with_constant(fldname).is_some() { 42 | return 1; 43 | } 44 | let fldname2 = self.pred.equates_with_field(fldname); 45 | if let Some(fldname2) = fldname2 { 46 | return cmp::min( 47 | self.p.distinct_values(fldname), 48 | self.p.distinct_values(&fldname2), 49 | ); 50 | } 51 | self.p.distinct_values(fldname) 52 | } 53 | 54 | fn schema(&self) -> Arc { 55 | self.p.schema() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/plan/tableplan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | metadata::{metadatamgr::MetadataMgr, statinfo::StatInfo}, 5 | query::scan::Scan, 6 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 7 | tx::transaction::{Transaction, TransactionError}, 8 | }; 9 | 10 | use super::plan::PlanControl; 11 | 12 | #[derive(Clone)] 13 | pub struct TablePlan { 14 | tblname: String, 15 | tx: Arc>, 16 | layout: Layout, 17 | si: StatInfo, 18 | } 19 | 20 | impl TablePlan { 21 | pub fn new( 22 | tx: Arc>, 23 | tblname: &str, 24 | md: Arc>, 25 | ) -> Result { 26 | let layout = md.lock().unwrap().get_layout(tblname, tx.clone())?; 27 | let si = md 28 | .lock() 29 | .unwrap() 30 | .get_stat_info(tblname, layout.clone(), tx.clone())?; 31 | Ok(TablePlan { 32 | tblname: tblname.to_string(), 33 | tx, 34 | layout, 35 | si, 36 | }) 37 | } 38 | } 39 | 40 | impl PlanControl for TablePlan { 41 | fn open(&self) -> Result { 42 | Ok(TableScan::new(self.tx.clone(), &self.tblname, self.layout.clone())?.into()) 43 | } 44 | 45 | fn blocks_accessed(&self) -> usize { 46 | self.si.blocks_accessed() 47 | } 48 | 49 | fn records_output(&self) -> usize { 50 | self.si.records_output() 51 | } 52 | 53 | fn distinct_values(&self, fldname: &str) -> usize { 54 | self.si.distinct_values(fldname) 55 | } 56 | 57 | fn schema(&self) -> Arc { 58 | self.layout.schema() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/plan/updateplanner.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use enum_dispatch::enum_dispatch; 4 | 5 | use crate::{ 6 | index::planner::indexupdateplanner::IndexUpdatePlanner, 7 | parse::{ 8 | createindexdata::CreateIndexData, createtabledata::CreateTableData, 9 | createviewdata::CreateViewData, deletedata::DeleteData, insertdata::InsertData, 10 | modifydata::ModifyData, 11 | }, 12 | tx::transaction::{Transaction, TransactionError}, 13 | }; 14 | 15 | use super::basicupdateplanner::BasicUpdatePlanner; 16 | 17 | #[enum_dispatch(UpdatePlanner)] 18 | pub trait UpdatePlannerControl { 19 | fn execute_insert( 20 | &self, 21 | data: &InsertData, 22 | tx: Arc>, 23 | ) -> Result; 24 | 25 | fn execute_delete( 26 | &self, 27 | data: &DeleteData, 28 | tx: Arc>, 29 | ) -> Result; 30 | 31 | fn execute_modify( 32 | &self, 33 | data: &ModifyData, 34 | tx: Arc>, 35 | ) -> Result; 36 | 37 | fn execute_create_table( 38 | &self, 39 | data: &CreateTableData, 40 | tx: Arc>, 41 | ) -> Result; 42 | 43 | fn execute_create_view( 44 | &self, 45 | data: &CreateViewData, 46 | tx: Arc>, 47 | ) -> Result; 48 | 49 | fn execute_create_index( 50 | &self, 51 | data: &CreateIndexData, 52 | tx: Arc>, 53 | ) -> Result; 54 | } 55 | 56 | #[enum_dispatch] 57 | pub enum UpdatePlanner { 58 | Basic(BasicUpdatePlanner), 59 | Index(IndexUpdatePlanner), 60 | } 61 | -------------------------------------------------------------------------------- /src/query.rs: -------------------------------------------------------------------------------- 1 | pub mod constant; 2 | pub mod expression; 3 | pub mod predicate; 4 | pub mod productscan; 5 | pub mod projectscan; 6 | pub mod projecttest; 7 | pub mod scan; 8 | pub mod scantest1; 9 | pub mod scantest2; 10 | pub mod selectscan; 11 | pub mod term; 12 | pub mod updatescan; 13 | -------------------------------------------------------------------------------- /src/query/constant.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Clone, Eq, Hash, PartialEq, PartialOrd)] 4 | pub struct Constant { 5 | ival: Option, 6 | sval: Option, 7 | } 8 | 9 | impl fmt::Display for Constant { 10 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 | if let Some(ival) = self.ival { 12 | return write!(f, "{}", ival); 13 | } 14 | if let Some(sval) = &self.sval { 15 | return write!(f, "{}", sval); 16 | } 17 | write!(f, "") 18 | } 19 | } 20 | 21 | impl Constant { 22 | pub fn with_int(ival: i32) -> Constant { 23 | Constant { 24 | ival: Some(ival), 25 | sval: None, 26 | } 27 | } 28 | 29 | pub fn with_string(sval: &str) -> Self { 30 | Constant { 31 | ival: None, 32 | sval: Some(sval.to_string()), 33 | } 34 | } 35 | 36 | pub fn as_int(&self) -> Option { 37 | self.ival 38 | } 39 | 40 | pub fn as_string(&self) -> Option { 41 | self.sval.clone() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/query/expression.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::{record::schema::Schema, tx::transaction::TransactionError}; 4 | 5 | use super::{constant::Constant, scan::ScanControl}; 6 | 7 | #[derive(Clone)] 8 | pub struct Expression { 9 | val: Option, 10 | fldname: Option, 11 | } 12 | 13 | impl fmt::Display for Expression { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | if let Some(val) = &self.val { 16 | return write!(f, "{}", val); 17 | } 18 | if let Some(fldname) = &self.fldname { 19 | return write!(f, "{}", fldname); 20 | } 21 | write!(f, "") 22 | } 23 | } 24 | 25 | impl Expression { 26 | pub fn with_constant(val: Constant) -> Expression { 27 | Expression { 28 | val: Some(val), 29 | fldname: None, 30 | } 31 | } 32 | 33 | pub fn with_string(fldname: &str) -> Expression { 34 | Expression { 35 | val: None, 36 | fldname: Some(fldname.to_string()), 37 | } 38 | } 39 | 40 | pub fn evaluate(&self, s: &mut T) -> Result { 41 | if let Some(val) = &self.val { 42 | return Ok(val.clone()); 43 | } 44 | if let Some(fldname) = &self.fldname { 45 | return s.get_val(fldname); 46 | } 47 | Err(TransactionError::General) 48 | } 49 | 50 | pub fn as_constant(&self) -> Option { 51 | self.val.clone() 52 | } 53 | 54 | pub fn as_field_name(&self) -> Option { 55 | self.fldname.clone() 56 | } 57 | 58 | pub fn applies_to(&self, sch: &Schema) -> bool { 59 | if self.val.is_some() { 60 | return true; 61 | } 62 | if let Some(fldname) = self.fldname.clone() { 63 | return sch.has_field(&fldname); 64 | } 65 | false 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/query/predicate.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, sync::Arc}; 2 | 3 | use crate::{plan::plan::Plan, record::schema::Schema, tx::transaction::TransactionError}; 4 | 5 | use super::{constant::Constant, scan::Scan, term::Term}; 6 | 7 | #[derive(Clone)] 8 | pub struct Predicate { 9 | terms: Vec, 10 | } 11 | 12 | impl fmt::Display for Predicate { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | write!( 15 | f, 16 | "{}", 17 | self.terms 18 | .iter() 19 | .map(|t| t.to_string()) 20 | .collect::>() 21 | .join(" and ") 22 | ) 23 | } 24 | } 25 | 26 | impl Predicate { 27 | pub fn new() -> Predicate { 28 | Predicate { terms: Vec::new() } 29 | } 30 | 31 | pub fn with_term(t: Term) -> Predicate { 32 | let terms = vec![t]; 33 | Predicate { terms } 34 | } 35 | 36 | pub fn conjoin_with(&mut self, pred: Predicate) { 37 | self.terms.extend(pred.terms) 38 | } 39 | 40 | pub fn is_satisfied(&self, s: &mut Scan) -> Result { 41 | for t in &self.terms { 42 | if !t.is_satisfied(s)? { 43 | return Ok(false); 44 | } 45 | } 46 | Ok(true) 47 | } 48 | 49 | pub fn reduction_factor(&self, p: &Plan) -> usize { 50 | let mut factor = 1; 51 | for t in self.terms.iter() { 52 | factor *= t.reduction_factor(p); 53 | } 54 | factor 55 | } 56 | 57 | pub fn select_sub_pred(&self, sch: Arc) -> Option { 58 | let mut result = Predicate::new(); 59 | for t in self.terms.iter() { 60 | if t.applies_to(&sch) { 61 | result.terms.push(t.clone()); 62 | } 63 | } 64 | if result.terms.is_empty() { 65 | return None; 66 | } 67 | Some(result) 68 | } 69 | 70 | pub fn join_sub_pred(&self, sch1: Arc, sch2: Arc) -> Option { 71 | let mut result = Predicate::new(); 72 | let mut newsch = Schema::new(); 73 | newsch.add_all(&sch1); 74 | newsch.add_all(&sch2); 75 | for t in self.terms.iter() { 76 | if !t.applies_to(&sch1) && !t.applies_to(&sch2) && t.applies_to(&newsch) { 77 | result.terms.push(t.clone()); 78 | } 79 | } 80 | if result.terms.is_empty() { 81 | return None; 82 | } 83 | Some(result) 84 | } 85 | 86 | pub fn equates_with_constant(&self, fldname: &str) -> Option { 87 | for t in self.terms.iter() { 88 | let c = t.equates_with_constant(fldname); 89 | if c.is_some() { 90 | return c; 91 | } 92 | } 93 | None 94 | } 95 | 96 | pub fn equates_with_field(&self, fldname: &str) -> Option { 97 | for t in self.terms.iter() { 98 | let s = t.equates_with_field(fldname); 99 | if s.is_some() { 100 | return s; 101 | } 102 | } 103 | None 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/query/productscan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{buffer::buffermgr::AbortError, tx::transaction::TransactionError}; 4 | 5 | use super::{ 6 | constant::Constant, 7 | scan::{Scan, ScanControl}, 8 | }; 9 | 10 | pub struct ProductScan { 11 | s1: Arc>, 12 | s2: Arc>, 13 | } 14 | 15 | impl ScanControl for ProductScan { 16 | fn before_first(&mut self) -> Result<(), TransactionError> { 17 | self.s1.lock().unwrap().before_first()?; 18 | self.s1.lock().unwrap().next()?; 19 | self.s2.lock().unwrap().before_first()?; 20 | Ok(()) 21 | } 22 | 23 | fn next(&mut self) -> Result { 24 | if self.s2.lock().unwrap().next()? { 25 | return Ok(true); 26 | } 27 | self.s2.lock().unwrap().before_first()?; 28 | Ok(self.s2.lock().unwrap().next()? && self.s1.lock().unwrap().next()?) 29 | } 30 | 31 | fn get_int(&mut self, fldname: &str) -> Result { 32 | if self.s1.lock().unwrap().has_field(fldname) { 33 | return self.s1.lock().unwrap().get_int(fldname); 34 | } 35 | self.s2.lock().unwrap().get_int(fldname) 36 | } 37 | 38 | fn get_string(&mut self, fldname: &str) -> Result { 39 | if self.s1.lock().unwrap().has_field(fldname) { 40 | return self.s1.lock().unwrap().get_string(fldname); 41 | } 42 | self.s2.lock().unwrap().get_string(fldname) 43 | } 44 | 45 | fn get_val(&mut self, fldname: &str) -> Result { 46 | if self.s1.lock().unwrap().has_field(fldname) { 47 | return self.s1.lock().unwrap().get_val(fldname); 48 | } 49 | self.s2.lock().unwrap().get_val(fldname) 50 | } 51 | 52 | fn has_field(&self, fldname: &str) -> bool { 53 | self.s1.lock().unwrap().has_field(fldname) || self.s2.lock().unwrap().has_field(fldname) 54 | } 55 | 56 | fn close(&mut self) -> Result<(), AbortError> { 57 | self.s1.lock().unwrap().close()?; 58 | self.s2.lock().unwrap().close()?; 59 | Ok(()) 60 | } 61 | } 62 | 63 | impl ProductScan { 64 | pub fn new( 65 | s1: Arc>, 66 | s2: Arc>, 67 | ) -> Result { 68 | let mut ps = ProductScan { s1, s2 }; 69 | ps.before_first()?; 70 | Ok(ps) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/query/projectscan.rs: -------------------------------------------------------------------------------- 1 | use crate::tx::transaction::TransactionError; 2 | 3 | use super::{ 4 | constant::Constant, 5 | scan::{Scan, ScanControl}, 6 | }; 7 | 8 | pub struct ProjectScan { 9 | s: Box, 10 | fieldlist: Vec, 11 | } 12 | 13 | impl ScanControl for ProjectScan { 14 | fn before_first(&mut self) -> Result<(), TransactionError> { 15 | self.s.before_first() 16 | } 17 | 18 | fn next(&mut self) -> Result { 19 | self.s.next() 20 | } 21 | 22 | fn get_int(&mut self, fldname: &str) -> Result { 23 | if self.has_field(fldname) { 24 | return self.s.get_int(fldname); 25 | } 26 | Err(TransactionError::General) 27 | } 28 | 29 | fn get_string(&mut self, fldname: &str) -> Result { 30 | if self.has_field(fldname) { 31 | return self.s.get_string(fldname); 32 | } 33 | Err(TransactionError::General) 34 | } 35 | 36 | fn get_val(&mut self, fldname: &str) -> Result { 37 | if self.has_field(fldname) { 38 | return self.s.get_val(fldname); 39 | } 40 | Err(TransactionError::General) 41 | } 42 | 43 | fn has_field(&self, fldname: &str) -> bool { 44 | self.fieldlist.contains(&fldname.to_string()) 45 | } 46 | 47 | fn close(&mut self) -> Result<(), crate::buffer::buffermgr::AbortError> { 48 | self.s.close() 49 | } 50 | } 51 | 52 | impl ProjectScan { 53 | pub fn new(s: Scan, fieldlist: Vec) -> ProjectScan { 54 | ProjectScan { 55 | s: Box::new(s), 56 | fieldlist, 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/query/projecttest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | query::{productscan::ProductScan, scan::ScanControl, updatescan::UpdateScanControl}, 10 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 11 | server::simpledb::SimpleDB, 12 | }; 13 | 14 | #[test] 15 | fn projecttest() { 16 | let db = SimpleDB::new("producttest").unwrap(); 17 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 18 | 19 | let mut sch1 = Schema::new(); 20 | sch1.add_int_field("A"); 21 | sch1.add_string_field("B", 9); 22 | let layout1 = Layout::new(Arc::new(sch1)); 23 | let mut ts1 = TableScan::new(tx.clone(), "T1", layout1.clone()).unwrap(); 24 | 25 | let mut sch2 = Schema::new(); 26 | sch2.add_int_field("C"); 27 | sch2.add_string_field("D", 9); 28 | let layout2 = Layout::new(Arc::new(sch2)); 29 | let mut ts2 = TableScan::new(tx.clone(), "T2", layout2.clone()).unwrap(); 30 | 31 | ts1.before_first().unwrap(); 32 | let n = 200; 33 | for i in 0..n { 34 | ts1.insert().unwrap(); 35 | ts1.set_int("A", i).unwrap(); 36 | ts1.set_string("B", &format!("aaa{}", i)).unwrap(); 37 | } 38 | ts1.close().unwrap(); 39 | 40 | ts2.before_first().unwrap(); 41 | for i in 0..n { 42 | ts2.insert().unwrap(); 43 | ts2.set_int("C", n - i - 1).unwrap(); 44 | ts2.set_string("D", &format!("bbb{}", n - i - 1)).unwrap(); 45 | } 46 | ts2.close().unwrap(); 47 | 48 | let s1 = TableScan::new(tx.clone(), "T1", layout1).unwrap(); 49 | let s2 = TableScan::new(tx.clone(), "T2", layout2).unwrap(); 50 | let mut s3 = ProductScan::new( 51 | Arc::new(Mutex::new(s1.into())), 52 | Arc::new(Mutex::new(s2.into())), 53 | ) 54 | .unwrap(); 55 | let mut count = 0; 56 | while s3.next().unwrap() { 57 | assert_eq!(format!("aaa{}", count / n), s3.get_string("B").unwrap()); 58 | count += 1; 59 | } 60 | s3.close().unwrap(); 61 | tx.lock().unwrap().commit().unwrap(); 62 | 63 | fs::remove_dir_all("producttest").unwrap(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/query/scan.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use crate::{ 4 | buffer::buffermgr::AbortError, 5 | index::query::{indexjoinscan::IndexJoinScan, indexselectscan::IndexSelectScan}, 6 | materialize::{groupbyscan::GroupByScan, mergejoinscan::MergeJoinScan, sortscan::SortScan}, 7 | multibuffer::{chunkscan::ChunkScan, multibufferproductscan::MultibufferProductScan}, 8 | record::tablescan::TableScan, 9 | tx::transaction::TransactionError, 10 | }; 11 | 12 | use super::{ 13 | constant::Constant, productscan::ProductScan, projectscan::ProjectScan, selectscan::SelectScan, 14 | }; 15 | 16 | #[enum_dispatch(Scan)] 17 | pub trait ScanControl { 18 | fn before_first(&mut self) -> Result<(), TransactionError>; 19 | fn next(&mut self) -> Result; 20 | fn get_int(&mut self, fldname: &str) -> Result; 21 | fn get_string(&mut self, fldname: &str) -> Result; 22 | fn get_val(&mut self, fldname: &str) -> Result; 23 | fn has_field(&self, fldname: &str) -> bool; 24 | fn close(&mut self) -> Result<(), AbortError>; 25 | } 26 | 27 | #[enum_dispatch] 28 | pub enum Scan { 29 | Product(ProductScan), 30 | Project(ProjectScan), 31 | Select(SelectScan), 32 | Table(TableScan), 33 | IndexSelect(IndexSelectScan), 34 | IndexJoin(IndexJoinScan), 35 | Chunk(ChunkScan), 36 | MultibufferProduct(MultibufferProductScan), 37 | Sort(SortScan), 38 | GroupBy(GroupByScan), 39 | MergeJoin(MergeJoinScan), 40 | } 41 | -------------------------------------------------------------------------------- /src/query/scantest1.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use rand::{distributions::Uniform, prelude::Distribution}; 9 | 10 | use crate::{ 11 | query::{ 12 | constant::Constant, expression::Expression, predicate::Predicate, 13 | projectscan::ProjectScan, scan::ScanControl, selectscan::SelectScan, term::Term, 14 | updatescan::UpdateScanControl, 15 | }, 16 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 17 | server::simpledb::SimpleDB, 18 | }; 19 | 20 | #[test] 21 | fn scantest1() { 22 | let db = SimpleDB::new("scantest1").unwrap(); 23 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 24 | 25 | let mut sch1 = Schema::new(); 26 | sch1.add_int_field("A"); 27 | sch1.add_string_field("B", 9); 28 | let layout1 = Layout::new(Arc::new(sch1)); 29 | let mut s1 = TableScan::new(tx.clone(), "T", layout1.clone()).unwrap(); 30 | 31 | s1.before_first().unwrap(); 32 | let n = 200; 33 | let mut rng = rand::thread_rng(); 34 | let die = Uniform::from(0..50); 35 | for _ in 0..n { 36 | s1.insert().unwrap(); 37 | let k = die.sample(&mut rng); 38 | s1.set_int("A", k).unwrap(); 39 | s1.set_string("B", &format!("rec{}", k)).unwrap(); 40 | } 41 | s1.close().unwrap(); 42 | 43 | let s2 = TableScan::new(tx.clone(), "T", layout1).unwrap(); 44 | let c = Constant::with_int(10); 45 | let t = Term::new(Expression::with_string("A"), Expression::with_constant(c)); 46 | let pred = Predicate::with_term(t); 47 | assert_eq!(format!("{}", pred), "A=10"); 48 | let s3 = SelectScan::new(s2.into(), pred); 49 | let fields = vec!["B".to_string()]; 50 | let mut s4 = ProjectScan::new(s3.into(), fields); 51 | let mut count = 0; 52 | while s4.next().unwrap() { 53 | assert_eq!(s4.get_string("B").unwrap(), "rec10"); 54 | count += 1; 55 | } 56 | assert!(count >= 0 && count <= n); 57 | s4.close().unwrap(); 58 | tx.lock().unwrap().commit().unwrap(); 59 | 60 | fs::remove_dir_all("scantest1").unwrap(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/query/scantest2.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::{ 9 | query::{ 10 | expression::Expression, predicate::Predicate, productscan::ProductScan, 11 | projectscan::ProjectScan, scan::ScanControl, selectscan::SelectScan, term::Term, 12 | updatescan::UpdateScanControl, 13 | }, 14 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 15 | server::simpledb::SimpleDB, 16 | }; 17 | 18 | #[test] 19 | fn scantest2() { 20 | let db = SimpleDB::new("scantest2").unwrap(); 21 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 22 | 23 | let mut sch1 = Schema::new(); 24 | sch1.add_int_field("A"); 25 | sch1.add_string_field("B", 9); 26 | let layout1 = Layout::new(Arc::new(sch1)); 27 | let mut us1 = TableScan::new(tx.clone(), "T1", layout1.clone()).unwrap(); 28 | us1.before_first().unwrap(); 29 | let n = 200; 30 | for i in 0..n { 31 | us1.insert().unwrap(); 32 | us1.set_int("A", i).unwrap(); 33 | us1.set_string("B", &format!("bbb{}", i)).unwrap(); 34 | } 35 | us1.close().unwrap(); 36 | 37 | let mut sch2 = Schema::new(); 38 | sch2.add_int_field("C"); 39 | sch2.add_string_field("D", 9); 40 | let layout2 = Layout::new(Arc::new(sch2)); 41 | let mut us2 = TableScan::new(tx.clone(), "T2", layout2.clone()).unwrap(); 42 | us2.before_first().unwrap(); 43 | for i in 0..n { 44 | us2.insert().unwrap(); 45 | us2.set_int("C", n - i - 1).unwrap(); 46 | us2.set_string("D", &format!("ddd{}", n - i - 1)).unwrap(); 47 | } 48 | us2.close().unwrap(); 49 | 50 | let s1 = TableScan::new(tx.clone(), "T1", layout1).unwrap(); 51 | let s2 = TableScan::new(tx.clone(), "T2", layout2).unwrap(); 52 | let s3 = ProductScan::new( 53 | Arc::new(Mutex::new(s1.into())), 54 | Arc::new(Mutex::new(s2.into())), 55 | ) 56 | .unwrap(); 57 | 58 | let t = Term::new(Expression::with_string("A"), Expression::with_string("C")); 59 | let pred = Predicate::with_term(t); 60 | assert_eq!(format!("{}", pred), "A=C"); 61 | let s4 = SelectScan::new(s3.into(), pred); 62 | 63 | let c = vec!["B".to_string(), "D".to_string()]; 64 | let mut s5 = ProjectScan::new(s4.into(), c); 65 | let mut count = 0; 66 | while s5.next().unwrap() { 67 | assert_eq!(s5.get_string("B").unwrap(), format!("bbb{}", count)); 68 | assert_eq!(s5.get_string("D").unwrap(), format!("ddd{}", count)); 69 | count += 1; 70 | } 71 | assert_eq!(count, 200); 72 | s5.close().unwrap(); 73 | tx.lock().unwrap().commit().unwrap(); 74 | 75 | fs::remove_dir_all("scantest2").unwrap(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/query/term.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp, fmt}; 2 | 3 | use crate::{ 4 | plan::plan::{Plan, PlanControl}, 5 | record::schema::Schema, 6 | tx::transaction::TransactionError, 7 | }; 8 | 9 | use super::{constant::Constant, expression::Expression, scan::Scan}; 10 | 11 | #[derive(Clone)] 12 | pub struct Term { 13 | lhs: Expression, 14 | rhs: Expression, 15 | } 16 | 17 | impl fmt::Display for Term { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}={}", self.lhs, self.rhs) 20 | } 21 | } 22 | 23 | impl Term { 24 | pub fn new(lhs: Expression, rhs: Expression) -> Term { 25 | Term { lhs, rhs } 26 | } 27 | 28 | pub fn is_satisfied(&self, s: &mut Scan) -> Result { 29 | let lhsval = self.lhs.evaluate(s)?; 30 | let rhsval = self.rhs.evaluate(s)?; 31 | Ok(lhsval == rhsval) 32 | } 33 | 34 | pub fn reduction_factor(&self, p: &Plan) -> usize { 35 | let lhs_name = self.lhs.as_field_name(); 36 | let rhs_name = self.rhs.as_field_name(); 37 | if let Some(lhs_name) = lhs_name.clone() { 38 | if let Some(rhs_name) = rhs_name { 39 | return cmp::max(p.distinct_values(&lhs_name), p.distinct_values(&rhs_name)); 40 | } 41 | } 42 | if let Some(lhs_name) = lhs_name { 43 | return p.distinct_values(&lhs_name); 44 | } 45 | if let Some(rhs_name) = rhs_name { 46 | return p.distinct_values(&rhs_name); 47 | } 48 | if self.lhs.as_constant() == self.rhs.as_constant() { 49 | return 1; 50 | } 51 | usize::MAX 52 | } 53 | 54 | pub fn equates_with_constant(&self, fldname: &str) -> Option { 55 | if let Some(lhs_name) = self.lhs.as_field_name() { 56 | if lhs_name != fldname { 57 | return None; 58 | } 59 | 60 | if let Some(rhs_name) = self.rhs.as_constant() { 61 | return Some(rhs_name); 62 | } 63 | } 64 | if let Some(rhs_name) = self.rhs.as_field_name() { 65 | if rhs_name != fldname { 66 | return None; 67 | } 68 | 69 | if let Some(lhs_name) = self.lhs.as_constant() { 70 | return Some(lhs_name); 71 | } 72 | } 73 | None 74 | } 75 | 76 | pub fn equates_with_field(&self, fldname: &str) -> Option { 77 | if let Some(lhs_name) = self.lhs.as_field_name() { 78 | if let Some(rhs_name) = self.rhs.as_field_name() { 79 | if lhs_name == fldname { 80 | return Some(rhs_name); 81 | } 82 | if rhs_name == fldname { 83 | return Some(lhs_name); 84 | } 85 | } 86 | } 87 | None 88 | } 89 | 90 | pub fn applies_to(&self, sch: &Schema) -> bool { 91 | self.lhs.applies_to(sch) && self.rhs.applies_to(sch) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/query/updatescan.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use crate::{ 4 | record::{rid::Rid, tablescan::TableScan}, 5 | tx::transaction::TransactionError, 6 | }; 7 | 8 | use super::{constant::Constant, selectscan::SelectScan}; 9 | 10 | #[enum_dispatch(UpdateScan)] 11 | pub trait UpdateScanControl { 12 | fn set_val(&mut self, fldname: &str, val: Constant) -> Result<(), TransactionError>; 13 | fn set_int(&mut self, fldname: &str, val: i32) -> Result<(), TransactionError>; 14 | fn set_string(&mut self, fldname: &str, val: &str) -> Result<(), TransactionError>; 15 | fn insert(&mut self) -> Result<(), TransactionError>; 16 | fn delete(&mut self) -> Result<(), TransactionError>; 17 | fn get_rid(&self) -> Option; 18 | fn move_to_rid(&mut self, rid: &Rid) -> Result<(), TransactionError>; 19 | } 20 | 21 | #[enum_dispatch] 22 | pub enum UpdateScan { 23 | Select(SelectScan), 24 | Table(TableScan), 25 | } 26 | -------------------------------------------------------------------------------- /src/record.rs: -------------------------------------------------------------------------------- 1 | pub mod layout; 2 | pub mod layouttest; 3 | pub mod recordpage; 4 | pub mod recordtest; 5 | pub mod rid; 6 | pub mod schema; 7 | pub mod tablescan; 8 | pub mod tablescantest; 9 | -------------------------------------------------------------------------------- /src/record/layout.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc}; 2 | 3 | use crate::file::page::Page; 4 | 5 | use super::schema::Schema; 6 | 7 | #[derive(Clone)] 8 | pub struct Layout { 9 | schema: Arc, 10 | offsets: HashMap, 11 | slotsize: usize, 12 | } 13 | 14 | impl Layout { 15 | pub fn new(schema: Arc) -> Layout { 16 | let offsets = HashMap::new(); 17 | let bytes = 4; 18 | let pos = bytes; 19 | 20 | let mut l = Layout { 21 | schema, 22 | offsets, 23 | slotsize: pos, 24 | }; 25 | 26 | for fldname in l.schema.fields() { 27 | l.offsets.insert(fldname.to_string(), l.slotsize); 28 | l.slotsize += l.length_in_bytes(fldname); 29 | } 30 | 31 | l 32 | } 33 | 34 | pub fn with_metadata( 35 | schema: Arc, 36 | offsets: HashMap, 37 | slotsize: usize, 38 | ) -> Layout { 39 | Layout { 40 | schema, 41 | offsets, 42 | slotsize, 43 | } 44 | } 45 | 46 | pub fn schema(&self) -> Arc { 47 | self.schema.clone() 48 | } 49 | 50 | pub fn offset(&self, fldname: &str) -> usize { 51 | self.offsets[fldname] 52 | } 53 | 54 | pub fn slot_size(&self) -> usize { 55 | self.slotsize 56 | } 57 | 58 | fn length_in_bytes(&self, fldname: &str) -> usize { 59 | let fldtype = self.schema.type_(fldname); 60 | let bytes = 4; 61 | match fldtype { 62 | super::schema::Type::Integer => bytes, 63 | super::schema::Type::Varchar => Page::max_length(self.schema.length(fldname)), 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/record/layouttest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::sync::Arc; 4 | 5 | use crate::record::{layout::Layout, schema::Schema}; 6 | 7 | #[test] 8 | fn layouttest() { 9 | let mut sch = Schema::new(); 10 | sch.add_int_field("A"); 11 | sch.add_string_field("B", 9); 12 | let layout = Layout::new(Arc::new(sch)); 13 | 14 | let e = [("A", 4), ("B", 8)]; 15 | for (i, fldname) in layout.schema().fields().iter().enumerate() { 16 | assert_eq!(fldname, e[i].0); 17 | 18 | let offset = layout.offset(fldname); 19 | assert_eq!(offset, e[i].1); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/record/recordtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use rand::{distributions::Uniform, prelude::Distribution}; 9 | 10 | use crate::{ 11 | record::{layout::Layout, recordpage::RecordPage, schema::Schema}, 12 | server::simpledb::SimpleDB, 13 | }; 14 | 15 | #[test] 16 | fn recordtest() { 17 | let db = SimpleDB::with_params("recordtest", 400, 8).unwrap(); 18 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 19 | 20 | let mut sch = Schema::new(); 21 | sch.add_int_field("A"); 22 | sch.add_string_field("B", 9); 23 | let layout = Layout::new(Arc::new(sch)); 24 | 25 | let e = [("A", 4), ("B", 8)]; 26 | for (i, fldname) in layout.schema().fields().iter().enumerate() { 27 | assert_eq!(fldname, e[i].0); 28 | 29 | let offset = layout.offset(fldname); 30 | assert_eq!(offset, e[i].1); 31 | } 32 | let blk = tx.lock().unwrap().append("testfile").unwrap(); 33 | tx.lock().unwrap().pin(&blk).unwrap(); 34 | let mut rp = RecordPage::new(tx.clone(), blk.clone(), layout).unwrap(); 35 | rp.format().unwrap(); 36 | 37 | let mut slot = rp.insert_after(None).unwrap(); 38 | let mut rng = rand::thread_rng(); 39 | let die = Uniform::from(0..50); 40 | while let Some(s) = slot { 41 | let n = die.sample(&mut rng); 42 | rp.set_int(s, "A", n).unwrap(); 43 | rp.set_string(s, "B", &format!("rec{}", n)).unwrap(); 44 | assert!(s <= 18); 45 | assert!(n >= 0 && n < 50); 46 | slot = rp.insert_after(slot).unwrap(); 47 | } 48 | 49 | let mut count = 0; 50 | slot = rp.next_after(None).unwrap(); 51 | while let Some(s) = slot { 52 | let a = rp.get_int(s, "A").unwrap(); 53 | let b = rp.get_string(s, "B").unwrap(); 54 | if a < 25 { 55 | count += 1; 56 | assert!(s <= 18); 57 | assert!(a < 25); 58 | assert_eq!(format!("rec{}", a), b); 59 | rp.delete(s).unwrap(); 60 | } 61 | slot = rp.next_after(slot).unwrap(); 62 | } 63 | assert!(count >= 0 && count <= 18); 64 | 65 | slot = rp.next_after(None).unwrap(); 66 | while let Some(s) = slot { 67 | let a = rp.get_int(s, "A").unwrap(); 68 | let b = rp.get_string(s, "B").unwrap(); 69 | assert!(s <= 18); 70 | assert!(a >= 25); 71 | assert_eq!(format!("rec{}", a), b); 72 | slot = rp.next_after(slot).unwrap(); 73 | } 74 | tx.lock().unwrap().unpin(&blk).unwrap(); 75 | tx.lock().unwrap().commit().unwrap(); 76 | 77 | fs::remove_dir_all("recordtest").unwrap(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/record/rid.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Clone, Eq, PartialEq)] 4 | pub struct Rid { 5 | blknum: i32, 6 | slot: usize, 7 | } 8 | 9 | impl fmt::Display for Rid { 10 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 | write!(f, "[{}, {}]", self.blknum, self.slot) 12 | } 13 | } 14 | 15 | impl Rid { 16 | pub fn new(blknum: i32, slot: usize) -> Rid { 17 | Rid { blknum, slot } 18 | } 19 | 20 | pub fn block_number(&self) -> i32 { 21 | self.blknum 22 | } 23 | 24 | pub fn slot(&self) -> usize { 25 | self.slot 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/record/schema.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Clone, Copy)] 4 | pub enum Type { 5 | Integer = 4, 6 | Varchar = 12, 7 | } 8 | 9 | pub struct Schema { 10 | fields: Vec, 11 | info: HashMap, 12 | } 13 | 14 | impl Schema { 15 | pub fn new() -> Schema { 16 | Schema { 17 | fields: Vec::new(), 18 | info: HashMap::new(), 19 | } 20 | } 21 | 22 | pub fn add_field(&mut self, fldname: &str, type_: Type, length: usize) { 23 | self.fields.push(fldname.to_string()); 24 | self.info 25 | .insert(fldname.to_string(), FieldInfo { type_, length }); 26 | } 27 | 28 | pub fn add_int_field(&mut self, fldname: &str) { 29 | self.add_field(fldname, Type::Integer, 0) 30 | } 31 | 32 | pub fn add_string_field(&mut self, fldname: &str, length: usize) { 33 | self.add_field(fldname, Type::Varchar, length) 34 | } 35 | 36 | pub fn add(&mut self, fldname: &str, sch: &Schema) { 37 | let type_ = sch.type_(fldname); 38 | let length = sch.length(fldname); 39 | self.add_field(fldname, type_, length) 40 | } 41 | 42 | pub fn add_all(&mut self, sch: &Schema) { 43 | for fldname in sch.fields() { 44 | self.add(fldname, sch) 45 | } 46 | } 47 | 48 | pub fn fields(&self) -> &Vec { 49 | &self.fields 50 | } 51 | 52 | pub fn has_field(&self, fldname: &str) -> bool { 53 | self.fields.contains(&fldname.to_string()) 54 | } 55 | 56 | pub fn type_(&self, fldname: &str) -> Type { 57 | self.info[fldname].type_ 58 | } 59 | 60 | pub fn length(&self, fldname: &str) -> usize { 61 | self.info[fldname].length 62 | } 63 | } 64 | 65 | struct FieldInfo { 66 | type_: Type, 67 | length: usize, 68 | } 69 | 70 | impl FieldInfo { 71 | #[allow(dead_code)] 72 | fn new(type_: Type, length: usize) -> FieldInfo { 73 | FieldInfo { type_, length } 74 | } 75 | 76 | #[allow(dead_code)] 77 | fn type_(&self) -> Type { 78 | self.type_ 79 | } 80 | 81 | #[allow(dead_code)] 82 | fn length(&self) -> usize { 83 | self.length 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/record/tablescantest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use rand::{distributions::Uniform, prelude::Distribution}; 9 | 10 | use crate::{ 11 | query::{scan::ScanControl, updatescan::UpdateScanControl}, 12 | record::{layout::Layout, schema::Schema, tablescan::TableScan}, 13 | server::simpledb::SimpleDB, 14 | }; 15 | 16 | #[test] 17 | fn tablescantest() { 18 | let db = SimpleDB::with_params("tabletest", 400, 8).unwrap(); 19 | let tx = Arc::new(Mutex::new(db.new_tx().unwrap())); 20 | 21 | let mut sch = Schema::new(); 22 | sch.add_int_field("A"); 23 | sch.add_string_field("B", 9); 24 | let layout = Layout::new(Arc::new(sch)); 25 | 26 | let e = [("A", 4), ("B", 8)]; 27 | for (i, fldname) in layout.schema().fields().iter().enumerate() { 28 | assert_eq!(fldname, e[i].0); 29 | 30 | let offset = layout.offset(fldname); 31 | assert_eq!(offset, e[i].1); 32 | } 33 | 34 | let mut ts = TableScan::new(tx.clone(), "T", layout.clone()).unwrap(); 35 | let mut rng = rand::thread_rng(); 36 | let die = Uniform::from(0..50); 37 | for i in 0..50 { 38 | ts.insert().unwrap(); 39 | let n = die.sample(&mut rng); 40 | ts.set_int("A", n).unwrap(); 41 | ts.set_string("B", &format!("rec{}", n)).unwrap(); 42 | 43 | let rid = ts.get_rid().unwrap(); 44 | assert_eq!(rid.block_number(), i / 19); 45 | assert_eq!(rid.slot(), i as usize % 19); 46 | assert!(n >= 0 && n < 50); 47 | } 48 | 49 | let mut count = 0; 50 | ts.before_first().unwrap(); 51 | while ts.next().unwrap() { 52 | let a = ts.get_int("A").unwrap(); 53 | let b = ts.get_string("B").unwrap(); 54 | if a < 25 { 55 | count += 1; 56 | 57 | let rid = ts.get_rid().unwrap(); 58 | 59 | let blknum = rid.block_number(); 60 | assert!(blknum >= 0 && blknum <= 2); 61 | 62 | let slot = rid.slot(); 63 | assert!(slot < 19); 64 | 65 | assert!(blknum * 19 + (slot as i32) < 50); 66 | 67 | assert!(a < 25); 68 | assert_eq!(format!("rec{}", a), b); 69 | 70 | ts.delete().unwrap(); 71 | } 72 | } 73 | assert!(count >= 0 && count <= 50); 74 | 75 | ts.before_first().unwrap(); 76 | while ts.next().unwrap() { 77 | let a = ts.get_int("A").unwrap(); 78 | let b = ts.get_string("B").unwrap(); 79 | 80 | let rid = ts.get_rid().unwrap(); 81 | 82 | let blknum = rid.block_number(); 83 | assert!(blknum >= 0 && blknum <= 2); 84 | 85 | let slot = rid.slot(); 86 | assert!(slot < 19); 87 | 88 | assert!(blknum * 19 + (slot as i32) < 50); 89 | 90 | assert!(a >= 25); 91 | assert_eq!(format!("rec{}", a), b); 92 | } 93 | ts.close().unwrap(); 94 | tx.lock().unwrap().commit().unwrap(); 95 | 96 | fs::remove_dir_all("tabletest").unwrap(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | pub mod simpledb; 2 | -------------------------------------------------------------------------------- /src/server/simpledb.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | use crate::buffer::buffermgr::BufferMgr; 5 | use crate::file::filemgr::FileMgr; 6 | use crate::index::planner::indexupdateplanner::IndexUpdatePlanner; 7 | use crate::log::logmgr::LogMgr; 8 | use crate::metadata::metadatamgr::MetadataMgr; 9 | use crate::opt::heuristicqueryplanner::HeuristicQueryPlanner; 10 | use crate::plan::planner::Planner; 11 | use crate::tx::transaction::{Transaction, TransactionError}; 12 | 13 | pub struct SimpleDB { 14 | fm: Arc, 15 | lm: Arc>, 16 | bm: Arc>, 17 | mdm: Option>>, 18 | planner: Option>>, 19 | } 20 | 21 | impl SimpleDB { 22 | const BLOCK_SIZE: usize = 400; 23 | const BUFFER_SIZE: usize = 8; 24 | const LOG_FILE: &'static str = "simpledb.log"; 25 | 26 | pub fn with_params( 27 | dirname: &str, 28 | blocksize: usize, 29 | buffsize: usize, 30 | ) -> Result { 31 | let fm = Arc::new(FileMgr::new(dirname, blocksize)?); 32 | let lm = Arc::new(Mutex::new(LogMgr::new(fm.clone(), SimpleDB::LOG_FILE)?)); 33 | let bm = Arc::new(Mutex::new(BufferMgr::new(fm.clone(), lm.clone(), buffsize))); 34 | Ok(SimpleDB { 35 | fm, 36 | lm, 37 | bm, 38 | mdm: None, 39 | planner: None, 40 | }) 41 | } 42 | 43 | pub fn new(dirname: &str) -> Result { 44 | let mut sd = SimpleDB::with_params(dirname, SimpleDB::BLOCK_SIZE, SimpleDB::BUFFER_SIZE)?; 45 | let tx = Arc::new(Mutex::new(sd.new_tx()?)); 46 | let isnew = sd.fm.is_new(); 47 | if isnew { 48 | println!("creating new database"); 49 | } else { 50 | println!("recovering existing database"); 51 | tx.lock().unwrap().recover()?; 52 | } 53 | let mdm = Arc::new(Mutex::new(MetadataMgr::new(isnew, tx.clone())?)); 54 | let qp = HeuristicQueryPlanner::new(mdm.clone()).into(); 55 | let up = IndexUpdatePlanner::new(mdm.clone()).into(); 56 | let planner = Planner::new(qp, up); 57 | sd.mdm = Some(mdm); 58 | sd.planner = Some(Arc::new(Mutex::new(planner))); 59 | tx.lock().unwrap().commit()?; 60 | Ok(sd) 61 | } 62 | 63 | pub fn new_tx(&self) -> Result { 64 | Transaction::new(self.fm.clone(), self.lm.clone(), self.bm.clone()) 65 | } 66 | 67 | #[allow(dead_code)] 68 | pub fn md_mgr(&self) -> Option>> { 69 | self.mdm.clone() 70 | } 71 | 72 | pub fn planner(&self) -> Option>> { 73 | self.planner.clone() 74 | } 75 | 76 | #[allow(dead_code)] 77 | pub fn file_mgr(&self) -> Arc { 78 | self.fm.clone() 79 | } 80 | 81 | #[allow(dead_code)] 82 | pub fn log_mgr(&mut self) -> Arc> { 83 | self.lm.clone() 84 | } 85 | 86 | #[allow(dead_code)] 87 | pub fn buffer_mgr(&mut self) -> Arc> { 88 | self.bm.clone() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/startserver.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use tonic::transport::Server; 4 | 5 | use crate::{ 6 | api::network::{ 7 | remoteconnection::RemoteConnection, 8 | remotemetadata::RemoteMetaData, 9 | remoteresultset::RemoteResultSet, 10 | simpledb::{ 11 | connection_server::ConnectionServer, meta_data_server::MetaDataServer, 12 | result_set_server::ResultSetServer, statement_server::StatementServer, 13 | }, 14 | }, 15 | server::simpledb::SimpleDB, 16 | }; 17 | 18 | mod api; 19 | mod buffer; 20 | mod file; 21 | mod index; 22 | mod log; 23 | mod materialize; 24 | mod metadata; 25 | mod multibuffer; 26 | mod opt; 27 | mod parse; 28 | mod plan; 29 | mod query; 30 | mod record; 31 | mod server; 32 | mod tx; 33 | 34 | #[tokio::main] 35 | async fn main() -> Result<(), Box> { 36 | let args: Vec = env::args().collect(); 37 | let dirname = if args.len() == 1 { 38 | "studentdb" 39 | } else { 40 | &args[1] 41 | }; 42 | let db = SimpleDB::new(dirname).unwrap(); 43 | 44 | let addr = "[::1]:1099".parse().unwrap(); 45 | 46 | let conn = RemoteConnection::new(db).unwrap(); 47 | let stmt = conn.create_statement(); 48 | let rs = RemoteResultSet::new(stmt.result_sets()); 49 | let md = RemoteMetaData::new(stmt.result_sets()); 50 | Server::builder() 51 | .add_service(ConnectionServer::new(conn)) 52 | .add_service(StatementServer::new(stmt)) 53 | .add_service(ResultSetServer::new(rs)) 54 | .add_service(MetaDataServer::new(md)) 55 | .serve(addr) 56 | .await?; 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /src/tx.rs: -------------------------------------------------------------------------------- 1 | pub mod bufferlist; 2 | pub mod concurrency; 3 | pub mod concurrencytest; 4 | pub mod recovery; 5 | pub mod transaction; 6 | pub mod txtest; 7 | -------------------------------------------------------------------------------- /src/tx/bufferlist.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | buffer::buffermgr::{AbortError, BufferMgr}, 8 | file::blockid::BlockId, 9 | }; 10 | 11 | pub struct BufferList { 12 | bm: Arc>, 13 | buffers: HashMap, 14 | pins: Vec, 15 | } 16 | 17 | impl BufferList { 18 | pub fn new(bm: Arc>) -> BufferList { 19 | BufferList { 20 | bm, 21 | buffers: HashMap::new(), 22 | pins: Vec::new(), 23 | } 24 | } 25 | 26 | pub(in crate::tx) fn get_index(&self, blk: &BlockId) -> Option { 27 | let idx = self.buffers.get(blk); 28 | if let Some(idx) = idx { 29 | return Some(*idx); 30 | } 31 | None 32 | } 33 | 34 | pub(in crate::tx) fn pin(&mut self, blk: &BlockId) -> Result<(), AbortError> { 35 | let idx = self.bm.lock().unwrap().pin(blk)?; 36 | self.buffers.insert(blk.clone(), idx); 37 | self.pins.push(blk.clone()); 38 | Ok(()) 39 | } 40 | 41 | pub(in crate::tx) fn unpin(&mut self, blk: &BlockId) -> Result<(), AbortError> { 42 | let idx = self.buffers.get(blk); 43 | if let Some(idx) = idx { 44 | self.bm.lock().unwrap().unpin(*idx); 45 | if let Some(pos) = self.pins.iter().position(|x| x == blk) { 46 | self.pins.remove(pos); 47 | } 48 | if !self.pins.contains(blk) { 49 | self.buffers.remove(blk); 50 | } 51 | return Ok(()); 52 | } 53 | Err(AbortError::General) 54 | } 55 | 56 | pub(in crate::tx) fn unpin_all(&mut self) { 57 | for blk in &self.pins { 58 | if let Some(idx) = self.buffers.get(blk) { 59 | self.bm.lock().unwrap().unpin(*idx); 60 | } 61 | } 62 | self.buffers.clear(); 63 | self.pins.clear(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/tx/concurrency.rs: -------------------------------------------------------------------------------- 1 | pub mod concurrencymgr; 2 | pub mod locktable; 3 | -------------------------------------------------------------------------------- /src/tx/concurrency/concurrencymgr.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::Lazy; 2 | use std::{collections::HashMap, sync::Mutex}; 3 | 4 | use crate::{ 5 | buffer::buffermgr::AbortError, file::blockid::BlockId, tx::concurrency::locktable::LockTable, 6 | }; 7 | 8 | static LOCKTBL: Lazy> = Lazy::new(|| Mutex::new(LockTable::new())); 9 | 10 | pub struct ConcurrencyMgr { 11 | locks: HashMap, 12 | } 13 | 14 | impl ConcurrencyMgr { 15 | pub fn new() -> ConcurrencyMgr { 16 | ConcurrencyMgr { 17 | locks: HashMap::new(), 18 | } 19 | } 20 | 21 | pub fn s_lock(&mut self, blk: &BlockId) -> Result<(), AbortError> { 22 | if !self.locks.contains_key(blk) { 23 | LOCKTBL.lock().unwrap().s_lock(blk)?; 24 | self.locks.insert(blk.clone(), "S".to_string()); 25 | } 26 | Ok(()) 27 | } 28 | 29 | pub fn x_lock(&mut self, blk: &BlockId) -> Result<(), AbortError> { 30 | if !self.has_x_lock(blk) { 31 | self.s_lock(blk)?; 32 | self.locks.insert(blk.clone(), "X".to_string()); 33 | } 34 | Ok(()) 35 | } 36 | 37 | pub fn release(&mut self) { 38 | for blk in self.locks.keys() { 39 | LOCKTBL.lock().unwrap().unlock(blk); 40 | } 41 | self.locks.clear(); 42 | } 43 | 44 | fn has_x_lock(&self, blk: &BlockId) -> bool { 45 | if let Some(locktype) = self.locks.get(blk) { 46 | if locktype == "X" { 47 | return true; 48 | } 49 | } 50 | false 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/tx/concurrency/locktable.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | thread::{current, park_timeout}, 4 | time::{Duration, SystemTime}, 5 | }; 6 | 7 | use crate::{ 8 | buffer::buffermgr::{waiting_too_long, AbortError}, 9 | file::blockid::BlockId, 10 | }; 11 | 12 | pub struct LockTable { 13 | locks: HashMap, 14 | max_time: u128, 15 | } 16 | 17 | impl LockTable { 18 | const MAX_TIME: u128 = 10000; 19 | 20 | pub fn new() -> LockTable { 21 | LockTable { 22 | locks: HashMap::new(), 23 | max_time: LockTable::MAX_TIME, 24 | } 25 | } 26 | 27 | pub fn s_lock(&mut self, blk: &BlockId) -> Result<(), AbortError> { 28 | let timestamp = SystemTime::now(); 29 | while self.has_x_lock(blk) && !waiting_too_long(timestamp, self.max_time)? { 30 | park_timeout(Duration::from_millis(self.max_time as u64)); 31 | } 32 | if self.has_x_lock(blk) { 33 | return Err(AbortError::General); 34 | } 35 | let val = self.get_lock_val(blk); 36 | self.locks.insert(blk.clone(), val + 1); 37 | Ok(()) 38 | } 39 | 40 | pub(in crate::tx::concurrency) fn unlock(&mut self, blk: &BlockId) { 41 | let val = self.get_lock_val(blk); 42 | if val > 1 { 43 | self.locks.insert(blk.clone(), val - 1); 44 | } else { 45 | self.locks.remove(blk); 46 | current().unpark(); 47 | } 48 | } 49 | 50 | fn has_x_lock(&self, blk: &BlockId) -> bool { 51 | self.get_lock_val(blk) < 0 52 | } 53 | 54 | fn get_lock_val(&self, blk: &BlockId) -> i32 { 55 | if let Some(ival) = self.locks.get(blk) { 56 | return *ival; 57 | } 58 | 0 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/tx/concurrencytest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{ 4 | fs, 5 | sync::{Arc, Mutex}, 6 | thread, 7 | time::Duration, 8 | }; 9 | 10 | use crate::{ 11 | buffer::buffermgr::BufferMgr, 12 | file::{blockid::BlockId, filemgr::FileMgr}, 13 | log::logmgr::LogMgr, 14 | server::simpledb::SimpleDB, 15 | tx::transaction::Transaction, 16 | }; 17 | 18 | #[test] 19 | fn concurrencytest() { 20 | let mut db = SimpleDB::with_params("concurrencytest", 400, 8).unwrap(); 21 | let fm = db.file_mgr(); 22 | let lm = db.log_mgr(); 23 | let bm = db.buffer_mgr(); 24 | 25 | let fm_a = fm.clone(); 26 | let lm_a = lm.clone(); 27 | let bm_a = bm.clone(); 28 | let handler_a = thread::spawn(move || run_a(fm_a, lm_a, bm_a)); 29 | 30 | let fm_b = fm.clone(); 31 | let lm_b = lm.clone(); 32 | let bm_b = bm.clone(); 33 | let handler_b = thread::spawn(move || run_b(fm_b, lm_b, bm_b)); 34 | 35 | let fm_c = fm.clone(); 36 | let lm_c = lm.clone(); 37 | let bm_c = bm.clone(); 38 | let handler_c = thread::spawn(move || run_c(fm_c, lm_c, bm_c)); 39 | 40 | handler_a.join().unwrap(); 41 | handler_b.join().unwrap(); 42 | handler_c.join().unwrap(); 43 | 44 | fs::remove_dir_all("concurrencytest").unwrap(); 45 | } 46 | 47 | fn run_a(fm: Arc, lm: Arc>, bm: Arc>) { 48 | let mut tx_a = Transaction::new(fm, lm, bm).unwrap(); 49 | let blk1 = BlockId::new("testfile", 1); 50 | let blk2 = BlockId::new("testfile", 2); 51 | tx_a.pin(&blk1).unwrap(); 52 | tx_a.pin(&blk2).unwrap(); 53 | 54 | tx_a.get_int(&blk1, 0).unwrap(); 55 | 56 | thread::sleep(Duration::from_millis(20)); 57 | 58 | tx_a.get_int(&blk2, 0).unwrap(); 59 | 60 | tx_a.commit().unwrap(); 61 | } 62 | 63 | fn run_b(fm: Arc, lm: Arc>, bm: Arc>) { 64 | let mut tx_b = Transaction::new(fm, lm, bm).unwrap(); 65 | let blk1 = BlockId::new("testfile", 1); 66 | let blk2 = BlockId::new("testfile", 2); 67 | tx_b.pin(&blk1).unwrap(); 68 | tx_b.pin(&blk2).unwrap(); 69 | 70 | tx_b.set_int(&blk2, 0, 0, false).unwrap(); 71 | 72 | thread::sleep(Duration::from_millis(20)); 73 | 74 | tx_b.get_int(&blk1, 0).unwrap(); 75 | 76 | tx_b.commit().unwrap(); 77 | } 78 | 79 | fn run_c(fm: Arc, lm: Arc>, bm: Arc>) { 80 | let mut tx_c = Transaction::new(fm, lm, bm).unwrap(); 81 | let blk1 = BlockId::new("testfile", 1); 82 | let blk2 = BlockId::new("testfile", 2); 83 | tx_c.pin(&blk1).unwrap(); 84 | tx_c.pin(&blk2).unwrap(); 85 | thread::sleep(Duration::from_millis(10)); 86 | 87 | tx_c.set_int(&blk1, 0, 0, false).unwrap(); 88 | 89 | thread::sleep(Duration::from_millis(20)); 90 | 91 | tx_c.get_int(&blk2, 0).unwrap(); 92 | 93 | tx_c.commit().unwrap(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/tx/recovery.rs: -------------------------------------------------------------------------------- 1 | pub mod checkpointrecord; 2 | pub mod commitrecord; 3 | pub mod logrecord; 4 | pub mod recoverymgr; 5 | pub mod recoverytest; 6 | pub mod rollbackrecord; 7 | pub mod setintrecord; 8 | pub mod setstringrecord; 9 | pub mod startrecord; 10 | -------------------------------------------------------------------------------- /src/tx/recovery/checkpointrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | file::page::Page, 8 | log::logmgr::LogMgr, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::logrecord::{LogRecord, Op}; 13 | 14 | pub struct CheckPointRecord {} 15 | 16 | impl LogRecord for CheckPointRecord { 17 | fn op(&self) -> Op { 18 | Op::CheckPoint 19 | } 20 | 21 | fn tx_number(&self) -> Option { 22 | None 23 | } 24 | 25 | fn undo(&self, _: &mut Transaction) -> Result<(), TransactionError> { 26 | Ok(()) 27 | } 28 | } 29 | 30 | impl CheckPointRecord { 31 | pub fn new() -> CheckPointRecord { 32 | CheckPointRecord {} 33 | } 34 | 35 | pub fn write_to_log(lm: &Arc>) -> Result { 36 | let bytes = 4; 37 | let mut rec = Vec::with_capacity(bytes); 38 | rec.resize(rec.capacity(), 0); 39 | let mut p = Page::with_vec(rec); 40 | p.set_int(0, Op::CheckPoint as i32); 41 | lm.lock().unwrap().append(p.contents()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/tx/recovery/commitrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | file::page::Page, 8 | log::logmgr::LogMgr, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::logrecord::{LogRecord, Op}; 13 | 14 | pub struct CommitRecord { 15 | txnum: usize, 16 | } 17 | 18 | impl LogRecord for CommitRecord { 19 | fn op(&self) -> Op { 20 | Op::Commit 21 | } 22 | 23 | fn tx_number(&self) -> Option { 24 | Some(self.txnum) 25 | } 26 | 27 | fn undo(&self, _: &mut Transaction) -> Result<(), TransactionError> { 28 | Ok(()) 29 | } 30 | } 31 | 32 | impl CommitRecord { 33 | pub fn new(p: Page) -> CommitRecord { 34 | let bytes = 4; 35 | let tpos = bytes; 36 | CommitRecord { 37 | txnum: p.get_int(tpos) as usize, 38 | } 39 | } 40 | 41 | pub fn write_to_log(lm: &Arc>, txnum: usize) -> Result { 42 | let bytes = 4; 43 | let mut rec = Vec::with_capacity(2 * bytes); 44 | rec.resize(rec.capacity(), 0); 45 | let mut p = Page::with_vec(rec); 46 | p.set_int(0, Op::Commit as i32); 47 | p.set_int(bytes, txnum as i32); 48 | lm.lock().unwrap().append(p.contents()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/tx/recovery/logrecord.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | file::page::Page, 3 | tx::transaction::{Transaction, TransactionError}, 4 | }; 5 | 6 | use super::{ 7 | checkpointrecord::CheckPointRecord, commitrecord::CommitRecord, rollbackrecord::RollbackRecord, 8 | setintrecord::SetIntRecord, setstringrecord::SetStringRecord, startrecord::StartRecord, 9 | }; 10 | 11 | #[derive(Eq, PartialEq)] 12 | pub enum Op { 13 | CheckPoint = 0, 14 | Start = 1, 15 | Commit = 2, 16 | Rollback = 3, 17 | SetInt = 4, 18 | SetString = 5, 19 | } 20 | 21 | pub trait LogRecord { 22 | fn op(&self) -> Op; 23 | fn tx_number(&self) -> Option; 24 | fn undo(&self, tx: &mut Transaction) -> Result<(), TransactionError>; 25 | } 26 | 27 | pub fn create_log_record(bytes: Vec) -> Result, TransactionError> { 28 | let p = Page::with_vec(bytes); 29 | match p.get_int(0) { 30 | x if x == Op::CheckPoint as i32 => Ok(Box::new(CheckPointRecord::new())), 31 | x if x == Op::Start as i32 => Ok(Box::new(StartRecord::new(p))), 32 | x if x == Op::Commit as i32 => Ok(Box::new(CommitRecord::new(p))), 33 | x if x == Op::Rollback as i32 => Ok(Box::new(RollbackRecord::new(p))), 34 | x if x == Op::SetInt as i32 => Ok(Box::new(SetIntRecord::new(p)?)), 35 | x if x == Op::SetString as i32 => Ok(Box::new(SetStringRecord::new(p)?)), 36 | _ => Err(TransactionError::General), 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/tx/recovery/recoverymgr.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | string::FromUtf8Error, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | use crate::{ 8 | buffer::{buffer::Buffer, buffermgr::BufferMgr}, 9 | log::logmgr::LogMgr, 10 | }; 11 | 12 | use super::{ 13 | commitrecord::CommitRecord, setintrecord::SetIntRecord, setstringrecord::SetStringRecord, 14 | startrecord::StartRecord, 15 | }; 16 | 17 | #[derive(Debug)] 18 | pub enum RecoveryError { 19 | IO(Error), 20 | Utf8(FromUtf8Error), 21 | General, 22 | } 23 | 24 | impl From for RecoveryError { 25 | fn from(e: Error) -> Self { 26 | RecoveryError::IO(e) 27 | } 28 | } 29 | 30 | impl From for RecoveryError { 31 | fn from(e: FromUtf8Error) -> Self { 32 | RecoveryError::Utf8(e) 33 | } 34 | } 35 | 36 | pub struct RecoveryMgr { 37 | lm: Arc>, 38 | bm: Arc>, 39 | txnum: usize, 40 | } 41 | 42 | impl RecoveryMgr { 43 | pub fn new( 44 | txnum: usize, 45 | lm: Arc>, 46 | bm: Arc>, 47 | ) -> Result { 48 | StartRecord::write_to_log(&lm, txnum)?; 49 | Ok(RecoveryMgr { lm, bm, txnum }) 50 | } 51 | 52 | pub fn commit(&self) -> Result<(), Error> { 53 | self.bm.lock().unwrap().flush_all(self.txnum)?; 54 | let lsn = CommitRecord::write_to_log(&self.lm, self.txnum)?; 55 | self.lm.lock().unwrap().flush(lsn)?; 56 | Ok(()) 57 | } 58 | 59 | pub fn set_int( 60 | &mut self, 61 | buff: &mut Buffer, 62 | offset: usize, 63 | _: i32, 64 | ) -> Result { 65 | let oldval = buff.contents().get_int(offset); 66 | let blk = buff.block(); 67 | if let Some(blk) = blk { 68 | let lsn = 69 | SetIntRecord::write_to_log(&self.lm, self.txnum, blk.clone(), offset, oldval)?; 70 | return Ok(lsn); 71 | } 72 | Err(RecoveryError::General) 73 | } 74 | 75 | pub fn set_string( 76 | &mut self, 77 | buff: &mut Buffer, 78 | offset: usize, 79 | _: &str, 80 | ) -> Result { 81 | let oldval = buff.contents().get_string(offset)?; 82 | let blk = buff.block(); 83 | if let Some(blk) = blk { 84 | let lsn = 85 | SetStringRecord::write_to_log(&self.lm, self.txnum, blk.clone(), offset, &oldval)?; 86 | return Ok(lsn); 87 | } 88 | Err(RecoveryError::General) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/tx/recovery/rollbackrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | file::page::Page, 8 | log::logmgr::LogMgr, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::logrecord::{LogRecord, Op}; 13 | 14 | pub struct RollbackRecord { 15 | txnum: usize, 16 | } 17 | 18 | impl LogRecord for RollbackRecord { 19 | fn op(&self) -> Op { 20 | Op::Rollback 21 | } 22 | 23 | fn tx_number(&self) -> Option { 24 | Some(self.txnum) 25 | } 26 | 27 | fn undo(&self, _: &mut Transaction) -> Result<(), TransactionError> { 28 | Ok(()) 29 | } 30 | } 31 | 32 | impl RollbackRecord { 33 | pub fn new(p: Page) -> RollbackRecord { 34 | let bytes = 4; 35 | let tpos = bytes; 36 | RollbackRecord { 37 | txnum: p.get_int(tpos) as usize, 38 | } 39 | } 40 | 41 | pub fn write_to_log(lm: &Arc>, txnum: usize) -> Result { 42 | let bytes = 4; 43 | let mut rec = Vec::with_capacity(2 * bytes); 44 | rec.resize(rec.capacity(), 0); 45 | let mut p = Page::with_vec(rec); 46 | p.set_int(0, Op::Rollback as i32); 47 | p.set_int(bytes, txnum as i32); 48 | lm.lock().unwrap().append(p.contents()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/tx/recovery/setintrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | string::FromUtf8Error, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | use crate::{ 8 | file::{blockid::BlockId, page::Page}, 9 | log::logmgr::LogMgr, 10 | tx::transaction::{Transaction, TransactionError}, 11 | }; 12 | 13 | use super::logrecord::{LogRecord, Op}; 14 | 15 | pub struct SetIntRecord { 16 | txnum: usize, 17 | offset: usize, 18 | val: i32, 19 | blk: BlockId, 20 | } 21 | 22 | impl LogRecord for SetIntRecord { 23 | fn op(&self) -> Op { 24 | Op::SetInt 25 | } 26 | 27 | fn tx_number(&self) -> Option { 28 | Some(self.txnum) 29 | } 30 | 31 | fn undo(&self, tx: &mut Transaction) -> Result<(), TransactionError> { 32 | tx.pin(&self.blk)?; 33 | tx.set_int(&self.blk, self.offset, self.val, false)?; 34 | tx.unpin(&self.blk)?; 35 | Ok(()) 36 | } 37 | } 38 | 39 | impl SetIntRecord { 40 | pub fn new(p: Page) -> Result { 41 | let bytes = 4; 42 | let tpos = bytes; 43 | let txnum = p.get_int(tpos) as usize; 44 | let fpos = tpos + bytes; 45 | let filename = p.get_string(fpos)?; 46 | let bpos = fpos + Page::max_length(filename.len()); 47 | let blknum = p.get_int(bpos); 48 | let blk = BlockId::new(&filename, blknum); 49 | let opos = bpos + bytes; 50 | let offset = p.get_int(opos) as usize; 51 | let vpos = opos + bytes; 52 | let val = p.get_int(vpos); 53 | Ok(SetIntRecord { 54 | txnum, 55 | offset, 56 | val, 57 | blk, 58 | }) 59 | } 60 | 61 | pub fn write_to_log( 62 | lm: &Arc>, 63 | txnum: usize, 64 | blk: BlockId, 65 | offset: usize, 66 | val: i32, 67 | ) -> Result { 68 | let bytes = 4; 69 | let tpos = bytes; 70 | let fpos = tpos + bytes; 71 | let bpos = fpos + Page::max_length(blk.file_name().len()); 72 | let opos = bpos + bytes; 73 | let vpos = opos + bytes; 74 | let mut rec = Vec::with_capacity(vpos + bytes); 75 | rec.resize(rec.capacity(), 0); 76 | let mut p = Page::with_vec(rec); 77 | p.set_int(0, Op::SetInt as i32); 78 | p.set_int(tpos, txnum as i32); 79 | p.set_string(fpos, blk.file_name()); 80 | p.set_int(bpos, blk.number()); 81 | p.set_int(opos, offset as i32); 82 | p.set_int(vpos, val); 83 | lm.lock().unwrap().append(p.contents()) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/tx/recovery/setstringrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | string::FromUtf8Error, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | use crate::{ 8 | file::{blockid::BlockId, page::Page}, 9 | log::logmgr::LogMgr, 10 | tx::transaction::{Transaction, TransactionError}, 11 | }; 12 | 13 | use super::logrecord::{LogRecord, Op}; 14 | 15 | pub struct SetStringRecord { 16 | txnum: usize, 17 | offset: usize, 18 | val: String, 19 | blk: BlockId, 20 | } 21 | 22 | impl LogRecord for SetStringRecord { 23 | fn op(&self) -> Op { 24 | Op::SetString 25 | } 26 | 27 | fn tx_number(&self) -> Option { 28 | Some(self.txnum) 29 | } 30 | 31 | fn undo(&self, tx: &mut Transaction) -> Result<(), TransactionError> { 32 | tx.pin(&self.blk)?; 33 | tx.set_string(&self.blk, self.offset, &self.val, false)?; 34 | tx.unpin(&self.blk)?; 35 | Ok(()) 36 | } 37 | } 38 | 39 | impl SetStringRecord { 40 | pub fn new(p: Page) -> Result { 41 | let bytes = 4; 42 | let tpos = bytes; 43 | let txnum = p.get_int(tpos) as usize; 44 | let fpos = tpos + bytes; 45 | let filename = p.get_string(fpos)?; 46 | let bpos = fpos + Page::max_length(filename.len()); 47 | let blknum = p.get_int(bpos); 48 | let blk = BlockId::new(&filename, blknum); 49 | let opos = bpos + bytes; 50 | let offset = p.get_int(opos) as usize; 51 | let vpos = opos + bytes; 52 | let val = p.get_string(vpos)?; 53 | Ok(SetStringRecord { 54 | txnum, 55 | offset, 56 | val, 57 | blk, 58 | }) 59 | } 60 | 61 | pub fn write_to_log( 62 | lm: &Arc>, 63 | txnum: usize, 64 | blk: BlockId, 65 | offset: usize, 66 | val: &str, 67 | ) -> Result { 68 | let bytes = 4; 69 | let tpos = bytes; 70 | let fpos = tpos + bytes; 71 | let bpos = fpos + Page::max_length(blk.file_name().len()); 72 | let opos = bpos + bytes; 73 | let vpos = opos + bytes; 74 | let reclen = vpos + Page::max_length(val.len()); 75 | let mut rec = Vec::with_capacity(reclen); 76 | rec.resize(rec.capacity(), 0); 77 | let mut p = Page::with_vec(rec); 78 | p.set_int(0, Op::SetString as i32); 79 | p.set_int(tpos, txnum as i32); 80 | p.set_string(fpos, blk.file_name()); 81 | p.set_int(bpos, blk.number()); 82 | p.set_int(opos, offset as i32); 83 | p.set_string(vpos, val); 84 | lm.lock().unwrap().append(p.contents()) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/tx/recovery/startrecord.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Error, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{ 7 | file::page::Page, 8 | log::logmgr::LogMgr, 9 | tx::transaction::{Transaction, TransactionError}, 10 | }; 11 | 12 | use super::logrecord::{LogRecord, Op}; 13 | 14 | pub struct StartRecord { 15 | txnum: usize, 16 | } 17 | 18 | impl LogRecord for StartRecord { 19 | fn op(&self) -> Op { 20 | Op::Start 21 | } 22 | 23 | fn tx_number(&self) -> Option { 24 | Some(self.txnum) 25 | } 26 | 27 | fn undo(&self, _: &mut Transaction) -> Result<(), TransactionError> { 28 | Ok(()) 29 | } 30 | } 31 | 32 | impl StartRecord { 33 | pub fn new(p: Page) -> StartRecord { 34 | let bytes = 4; 35 | let tpos = bytes; 36 | StartRecord { 37 | txnum: p.get_int(tpos) as usize, 38 | } 39 | } 40 | 41 | pub fn write_to_log(lm: &Arc>, txnum: usize) -> Result { 42 | let bytes = 4; 43 | let mut rec = Vec::with_capacity(2 * bytes); 44 | rec.resize(rec.capacity(), 0); 45 | let mut p = Page::with_vec(rec); 46 | p.set_int(0, Op::Start as i32); 47 | p.set_int(bytes, txnum as i32); 48 | lm.lock().unwrap().append(p.contents()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/tx/txtest.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::fs; 4 | 5 | use crate::{file::blockid::BlockId, server::simpledb::SimpleDB, tx::transaction::Transaction}; 6 | 7 | #[test] 8 | fn txtest() { 9 | let mut db = SimpleDB::with_params("txtest", 400, 8).unwrap(); 10 | let fm = db.file_mgr(); 11 | let lm = db.log_mgr(); 12 | 13 | let bm = db.buffer_mgr(); 14 | 15 | let mut tx1 = Transaction::new(fm.clone(), lm.clone(), bm.clone()).unwrap(); 16 | let blk = BlockId::new("testfile", 1); 17 | tx1.pin(&blk).unwrap(); 18 | tx1.set_int(&blk, 80, 1, false).unwrap(); 19 | tx1.set_string(&blk, 40, "one", false).unwrap(); 20 | tx1.commit().unwrap(); 21 | 22 | let mut tx2 = Transaction::new(fm.clone(), lm.clone(), bm.clone()).unwrap(); 23 | tx2.pin(&blk).unwrap(); 24 | let ival = tx2.get_int(&blk, 80).unwrap(); 25 | let sval = tx2.get_string(&blk, 40).unwrap(); 26 | assert_eq!(1, ival); 27 | assert_eq!("one", sval); 28 | let newival = ival + 1; 29 | let newsval = sval + "!"; 30 | tx2.set_int(&blk, 80, newival, true).unwrap(); 31 | tx2.set_string(&blk, 40, &newsval, true).unwrap(); 32 | tx2.commit().unwrap(); 33 | let mut tx3 = Transaction::new(fm.clone(), lm.clone(), bm.clone()).unwrap(); 34 | tx3.pin(&blk).unwrap(); 35 | assert_eq!(2, tx3.get_int(&blk, 80).unwrap()); 36 | assert_eq!("one!", tx3.get_string(&blk, 40).unwrap()); 37 | tx3.set_int(&blk, 80, 9999, true).unwrap(); 38 | assert_eq!(9999, tx3.get_int(&blk, 80).unwrap()); 39 | tx3.rollback().unwrap(); 40 | 41 | let mut tx4 = Transaction::new(fm.clone(), lm.clone(), bm.clone()).unwrap(); 42 | tx4.pin(&blk).unwrap(); 43 | assert_eq!(2, tx4.get_int(&blk, 80).unwrap()); 44 | tx4.commit().unwrap(); 45 | 46 | fs::remove_dir_all("txtest").unwrap(); 47 | } 48 | } 49 | --------------------------------------------------------------------------------