diff --git a/READme.md b/READme.md index a523f33..be68c62 100644 --- a/READme.md +++ b/READme.md @@ -11,8 +11,28 @@ https://www.sqlite.org/lang_insert.html ////////////////////////////////////////// database page +page size 2 bytes +locked 1 bytes +File change counter 4 bytes +database pages 4 bytes +page size 2 bytes +// btree data later +/////////////////////////////////////////// tables page +page is tables page 1 byte // 1 -/////////////////////////////////////////// first tables page \ No newline at end of file + + + + + + + + + + + +/////////////////////////////////////////// data pages +page is tables page 1 byte // 0 \ No newline at end of file diff --git a/src/commands/sql_command.rs b/src/commands/sql_command.rs index d383edc..8c5c606 100644 --- a/src/commands/sql_command.rs +++ b/src/commands/sql_command.rs @@ -1,5 +1,3 @@ -use std::fs::File; - use crate::database::database::Database; use crate::parse; use crate::parse::parser::{Parser, ParserError, Statement}; @@ -31,8 +29,8 @@ impl SQLCommand { } } -pub fn run_sql_command(command: String, database: &mut Database, file: &File) -> Result { - match parse::parse(command.trim().to_string(), database, file) { +pub fn run_sql_command(command: String, database: &mut Database) -> Result { + match parse::parse(command.trim().to_string(), database) { Ok(msg) => return Ok(format!("{}", msg)), Err(msg) => return Err(format!("{}", msg)), }; diff --git a/src/database/database.rs b/src/database/database.rs index 1b428fa..53af9ed 100644 --- a/src/database/database.rs +++ b/src/database/database.rs @@ -1,4 +1,6 @@ use std::collections::HashMap; +use std::fs::File; +use std::io::{SeekFrom, Seek, Write}; use std::path::Path; use crate::constants::{ PAGE_SIZE @@ -15,6 +17,7 @@ pub struct DatabaseMetaData { #[derive(Debug)] pub struct Database { pub name: String, + pub file: File, pub pager: pager::Pager, pub tables: HashMap, } @@ -22,11 +25,13 @@ pub struct Database { impl Database { - pub fn new(name: String) -> Self { + pub fn new(name: String, file: File) -> Self { // here we read it from the file // temporary values + // get database data and tables from the first pages in file and fill tables hashmap Database { name: name.clone(), + file: file, pager: pager::new(name), tables: HashMap::new(), } @@ -43,6 +48,19 @@ impl Database { } } + pub fn fetch_page(&self) { + + } + + pub fn close_database(&mut self) { + for (i, page) in self.pager.pages.iter().enumerate() { + if (page != &[0; PAGE_SIZE]) { + self.file.seek(SeekFrom::Start((i * PAGE_SIZE) as u64)).unwrap(); + self.file.write_all(&self.pager.pages[i]).unwrap(); + } + } + } + //pub fn get_table() {} } diff --git a/src/database/pager.rs b/src/database/pager.rs index ca752ac..c1422b4 100644 --- a/src/database/pager.rs +++ b/src/database/pager.rs @@ -1,4 +1,4 @@ -use std::{path::Path, fs::File}; +use std::{path::Path, fs::{File, OpenOptions}, io::{Seek, SeekFrom, Read}}; use crate::constants::{ PAGE_SIZE, @@ -9,11 +9,10 @@ use crate::constants::{ #[derive(Debug)] pub struct Pager { + // maybe a hashmap better pub pages: [[u8; PAGE_SIZE]; MAX_PAGES], pub file_length: usize, pub file_desc: usize, - pub database_page: [u8; PAGE_SIZE],//[u8; PAGE_SIZE], - pub tables_pages: Vec<[u8; PAGE_SIZE]>,//[u8; PAGE_SIZE], } @@ -22,23 +21,35 @@ pub fn new(name: String) -> Pager{ let path = Path::new(&name); - let mut file = File::create(path).unwrap(); - - + let file = OpenOptions::new() + .read(true) + .open(path) + .unwrap(); + + // maybe get database abd tables pages and put them in pager let ff = Pager{ + // uninitializes pages inited later + // act as cache pages: [[0; PAGE_SIZE]; MAX_PAGES], file_length: file.metadata().unwrap().len() as usize / PAGE_SIZE, file_desc: 0, - database_page: [0; PAGE_SIZE], - tables_pages: Vec::from([[0; PAGE_SIZE]]), }; ff } impl Pager{ - - pub fn get_page(&self){ - + pub fn get_page(&mut self, page_number: usize, file: &mut File){ + if page_number > MAX_PAGES { + // erooooooooooooooor + } + if self.pages[page_number] == [0; PAGE_SIZE]{ + file.seek(SeekFrom::Start((page_number * PAGE_SIZE) as u64)).unwrap(); + let mut buf = [0; PAGE_SIZE]; + file.read_exact(&mut buf).unwrap(); + self.pages[page_number] = buf; + }else{ + + } } pub fn add_page(&self){ diff --git a/src/database/table.rs b/src/database/table.rs index 9ddbe8f..7f07683 100644 --- a/src/database/table.rs +++ b/src/database/table.rs @@ -10,25 +10,25 @@ use super::database::Database; #[derive(Debug)] pub struct Table { - name: String, - columns: Vec, - last_id: u64, - primary_key: Option, + pub name: String, + pub columns: Vec, + pub last_id: u64, + pub primary_key: Option, } #[derive(Debug)] pub struct Column { - name: String, - data_type: DataType, - is_pk: bool, - is_unique: bool, - nullable: bool, + pub name: String, + pub data_type: DataType, + pub is_pk: bool, + pub is_unique: bool, + pub nullable: bool, } use std::fs::{File, OpenOptions}; impl Table { - pub fn new(params: (String, Vec), database: &mut Database, mut file: &File) -> Result { + pub fn new(params: (String, Vec), database: &mut Database) -> Result { let mut table_string = String::from("table"); table_string.push_str(&" "); table_string.push_str(¶ms.0); @@ -76,17 +76,16 @@ impl Table { nullable: !col.not_null, }) } - println!("{:?}", table_string); table_string = table_string[0..table_string.len() - 2].to_string(); table_string.push_str(&");"); - println!("{:?}", table_string); - + let mut tables_pages = &database.file; - file.seek(SeekFrom::Start(4096)).unwrap(); - file.write_all(table_string.as_bytes()).unwrap(); + //tables_pages.seek(SeekFrom::Start(4096)).unwrap(); + //tables_pages.write_all(table_string.as_bytes()).unwrap(); + //tables_pages.write_all(&[0]).unwrap(); Ok(Table { name: params.0, diff --git a/src/main.rs b/src/main.rs index de080db..3841f43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,34 +29,35 @@ fn main() -> rustyline::Result<()> { } let mut database = if Path::new(&args[1]).exists() { - database::database::Database::new(args[1].to_string()) + let mut file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(&args[1]) + .unwrap(); + + + // init file + if file.metadata().unwrap().len() == 0 { + // write database page + // add metadata from databases page + file.write_all(&[0; 4096]).unwrap(); + // write tables page + file.write_all(&[255; 4096]).unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(b"ilyes's database").unwrap(); + // add metadata from table page + } + database::database::Database::new(args[1].to_string(), file) }else{ println!("invalid database file"); process::exit(1) }; - let mut file = OpenOptions::new() - .read(true) - .write(true) - .open(&args[1]) - .unwrap(); - - println!("{:?}", Path::new(&args[1]).metadata().unwrap().len()); - - // init file - if file.metadata().unwrap().len() == 0 { - // write database page - // add metadata from databases page - file.write_all(&[0; 4096]).unwrap(); - // write tables page - file.write_all(&[255; 4096]).unwrap(); - file.seek(SeekFrom::Start(0)).unwrap(); - file.write_all(b"ilyes's database").unwrap(); - // add metadata from table page - } - println!("{:?}", file.metadata().unwrap().len()); - //let mut rl = DefaultEditor::new()?; + + + let config = get_config(); let helper = REPLHelper::default(); let mut repl = Editor::with_config(config).unwrap(); @@ -80,7 +81,7 @@ fn main() -> rustyline::Result<()> { match process_command(&command) { CommandType::TypeSQL(cmd) => match cmd { SQLCommand::Invalid(err) => println!("an error occured: {}", err), - _ => match run_sql_command(command, &mut database, &file) { + _ => match run_sql_command(command, &mut database) { Ok(result) => println!("{}", result), Err(err) => println!("an error occured: \n{}", err), }, diff --git a/src/parse/mod.rs b/src/parse/mod.rs index bfd4f1a..cd901d3 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -9,7 +9,7 @@ use crate::database::{database::Database, table::Table}; use self::parser::ColumnDef; -pub fn parse(command: String, database: &mut Database, file: &File) -> Result { +pub fn parse(command: String, database: &mut Database) -> Result { // this bclock is returned //parser::Parser::new(command); match parser::Parser::parse(command) { @@ -39,7 +39,7 @@ pub fn parse(command: String, database: &mut Database, file: &File) -> Result validate_delete((table_name, selection), database), Statement::CreateTable { name, columns } => { - validate_create((name, columns), database, file) + validate_create((name, columns), database) } Statement::Drop { object_type, names } => { validate_drop((object_type, names), database) @@ -76,13 +76,12 @@ pub fn parse(command: String, database: &mut Database, file: &File) -> Result), database: &mut Database, - file: &File ) -> Result { match database.has_table(¶ms.0) { true => Err(String::from("table already exists")), false => { let table_name = params.0.to_string(); - let table = Table::new(params, database, file)?; + let table = Table::new(params, database)?; table.show_table_structure(); database.tables.insert(table_name, table); //println!("{:?}", database); @@ -144,9 +143,40 @@ fn validate_insert( params: (String, bool, Option>, Vec), database: &mut Database, ) -> Result { - match check_table_exist("table_name".to_string()) { - true => Ok(String::from("dazdazd")), - false => Err(String::from("table doesnt exist")), + match database.has_table(¶ms.0) { + false => Err(String::from("table doesnt exists")), + true => { + + let table = database.tables.get(¶ms.0).unwrap(); + println!("{:?}", table); + println!("///////////////////////////////////////////{:?}", params); + if params.2 == None { + if params.2.unwrap().len() != table.columns.len() { + return Err(String::from("invalid columns selection")) + } + }else{ + for col in params.2.unwrap() { + for col_valid in &table.columns { + if col == col_valid.name { + // col is valid + } + } + } + // check columns number and names + } + // validate cols + + // validate primary keys + + // validate nulls + + // validate datatypes + + + + // add data to pages + //let cur_page = database.pager.pages[2]; + Ok(String::from("dazdazd"))} } }