filtered column names and delete
This commit is contained in:
parent
244f339460
commit
a26ea16f55
|
@ -14,9 +14,9 @@ This is a SQL database written in Rust. It will be based off of (and hopefully b
|
|||
|
||||
[X] SELECT * query
|
||||
|
||||
[ ] DELETE command
|
||||
[x] SELECT (filtered columns) query
|
||||
|
||||
[ ] SELECT (filtered columns) query
|
||||
[x] DELETE command
|
||||
|
||||
[ ] WHERE clause for SELECT and DELETE
|
||||
|
||||
|
|
1
squirrel-client/.gitignore
vendored
Normal file
1
squirrel-client/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1
squirrel-server/.gitignore
vendored
1
squirrel-server/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/data
|
||||
|
|
|
@ -3,12 +3,13 @@ use std::fs;
|
|||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::net::{Shutdown, TcpListener, TcpStream};
|
||||
use std::thread;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod parser;
|
||||
pub use parser::command::Command;
|
||||
|
||||
mod table;
|
||||
use parser::command::{CreateCommand, InsertCommand, SelectCommand};
|
||||
use parser::command::{CreateCommand, InsertCommand, SelectCommand, DeleteCommand};
|
||||
pub use table::datatypes::Datatype;
|
||||
pub use table::table_definition::{ColumnDefinition, TableDefinition};
|
||||
|
||||
|
@ -86,31 +87,80 @@ fn handle_insert(command: InsertCommand) -> ::anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_delete(command: DeleteCommand) -> ::anyhow::Result<String> {
|
||||
let mut file = fs::File::open(format!("./data/blobs/{}", command.table_name))?;
|
||||
let tabledef = read_tabledef(command.table_name.clone())?;
|
||||
let mut buf: Vec<u8> = vec![0; tabledef.get_byte_size()];
|
||||
let mut row_count: usize = 0;
|
||||
|
||||
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
||||
row_count += 1;
|
||||
}
|
||||
|
||||
let _ = fs::remove_file(format!("./data/blobs/{}", command.table_name));
|
||||
|
||||
let _ = fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(format!("./data/blobs/{}", command.table_name))?;
|
||||
|
||||
return Ok(format!("{} Rows Deleted", row_count));
|
||||
}
|
||||
|
||||
fn handle_select(command: SelectCommand) -> ::anyhow::Result<String> {
|
||||
let mut file = fs::File::open(format!("./data/blobs/{}", command.table_name))?;
|
||||
let tabledef = read_tabledef(command.table_name)?;
|
||||
let mut response = String::new();
|
||||
let mut column_names: Vec<String> = vec![];
|
||||
|
||||
for col_name in &command.column_names {
|
||||
if col_name == "*" {
|
||||
for col_defs in &tabledef.column_defs {
|
||||
column_names.push(col_defs.name.clone());
|
||||
}
|
||||
} else {
|
||||
column_names.push(col_name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
response += "| ";
|
||||
for col_def in &tabledef.column_defs {
|
||||
response += format!("{} | ", col_def.name).as_str();
|
||||
for col_name in &column_names {
|
||||
response += format!("{} | ", col_name).as_str();
|
||||
}
|
||||
response += "\n";
|
||||
response += "-----------\n";
|
||||
let mut buf: Vec<u8> = vec![0; tabledef.get_byte_size()];
|
||||
|
||||
let mut table: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut num_rows: usize = 0;
|
||||
|
||||
for col_name in &column_names {
|
||||
table.insert(col_name.clone(), vec![]);
|
||||
}
|
||||
|
||||
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
||||
response += "| ";
|
||||
let mut idx = 0;
|
||||
let mut idx: usize = 0;
|
||||
for col_def in &tabledef.column_defs {
|
||||
let len = if col_def.length > 0 {
|
||||
col_def.length
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
||||
response += format!("{} | ", str_val).as_str();
|
||||
if column_names.iter().any(|col_name| &col_def.name == col_name) {
|
||||
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
||||
table.get_mut(&col_def.name).unwrap().push(str_val);
|
||||
}
|
||||
idx += len;
|
||||
}
|
||||
num_rows += 1;
|
||||
}
|
||||
|
||||
for i in 0..num_rows {
|
||||
response += "| ";
|
||||
for col_name in &column_names {
|
||||
response += format!("{} | ", table.get(col_name).unwrap()[i]).as_str();
|
||||
}
|
||||
response += "\n";
|
||||
}
|
||||
|
||||
|
@ -125,6 +175,8 @@ fn run_command(query: String) -> ::anyhow::Result<String> {
|
|||
|
||||
let command: Command = Command::from_string(query)?;
|
||||
|
||||
println!("Parsed Command: {:?}", command);
|
||||
|
||||
match command {
|
||||
Command::Create(create_command) => {
|
||||
let result = handle_create(create_command);
|
||||
|
@ -150,7 +202,14 @@ fn run_command(query: String) -> ::anyhow::Result<String> {
|
|||
Ok(result.err().unwrap().to_string())
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow!("Invalid command")),
|
||||
Command::Delete(delete_command) => {
|
||||
let result = handle_delete(delete_command);
|
||||
if result.is_ok() {
|
||||
Ok(result?)
|
||||
} else {
|
||||
Ok(result.err().unwrap().to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,8 +219,13 @@ fn handle_client(mut stream: TcpStream) -> ::anyhow::Result<()> {
|
|||
while match stream.read(&mut data) {
|
||||
Ok(_size) => {
|
||||
let query_string = String::from_utf8(data.to_vec())?;
|
||||
let response: String = run_command(query_string)?;
|
||||
let response_res: ::anyhow::Result<String> = run_command(query_string);
|
||||
|
||||
let response = match response_res {
|
||||
Ok(result) => result,
|
||||
Err(err_msg) => String::from(format!("Error: {}", err_msg.to_string()))
|
||||
};
|
||||
|
||||
let response_data_size = response.len().to_le_bytes();
|
||||
stream.write_all(&response_data_size)?; // send length of message
|
||||
stream.write_all(response.as_bytes())?; // send message
|
||||
|
|
|
@ -9,7 +9,7 @@ pub enum Command {
|
|||
Select(SelectCommand),
|
||||
Create(CreateCommand),
|
||||
Insert(InsertCommand),
|
||||
Delete,
|
||||
Delete(DeleteCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
@ -17,6 +17,11 @@ pub struct CreateCommand {
|
|||
pub table_definition: TableDefinition,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct DeleteCommand {
|
||||
pub table_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct InsertCommand {
|
||||
pub table_name: String,
|
||||
|
@ -26,7 +31,7 @@ pub struct InsertCommand {
|
|||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct SelectCommand {
|
||||
pub table_name: String,
|
||||
// TODO Later: pub column_names: Vec<String>,
|
||||
pub column_names: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
@ -47,7 +52,13 @@ enum CreateParserState {
|
|||
}
|
||||
|
||||
enum SelectParserState {
|
||||
Wildcard, // Temporary, col selection coming soon
|
||||
ColumnName,
|
||||
ColumnNameCommaOrFrom,
|
||||
TableName,
|
||||
Semicolon,
|
||||
}
|
||||
|
||||
enum DeleteParserState {
|
||||
FromKeyword,
|
||||
TableName,
|
||||
Semicolon,
|
||||
|
@ -208,25 +219,29 @@ impl Command {
|
|||
}
|
||||
|
||||
fn parse_select_command(tokens: &mut Vec<String>) -> ::anyhow::Result<Command> {
|
||||
let mut state: SelectParserState = SelectParserState::Wildcard;
|
||||
let mut state: SelectParserState = SelectParserState::ColumnName;
|
||||
|
||||
// intermediate tmp vars
|
||||
let mut table_name = String::new();
|
||||
let mut column_names: Vec<String> = vec![];
|
||||
|
||||
while let Some(token) = &tokens.pop() {
|
||||
match state {
|
||||
SelectParserState::Wildcard => {
|
||||
if token != "*" {
|
||||
return Err(anyhow!("Expected to find selection at or near '{}' (SQUIRREL does not support column seletion)", token));
|
||||
SelectParserState::ColumnName => {
|
||||
if token.eq_ignore_ascii_case("FROM") {
|
||||
return Err(anyhow!("Did not expect FROM keyword at or near '{}'", token));
|
||||
} else {
|
||||
state = SelectParserState::FromKeyword;
|
||||
column_names.push(token.clone());
|
||||
state = SelectParserState::ColumnNameCommaOrFrom;
|
||||
}
|
||||
}
|
||||
SelectParserState::FromKeyword => {
|
||||
if !token.eq_ignore_ascii_case("FROM") {
|
||||
return Err(anyhow!("Expected to find FROM at or near '{}'", token));
|
||||
} else {
|
||||
SelectParserState::ColumnNameCommaOrFrom => {
|
||||
if token == "," {
|
||||
state = SelectParserState::ColumnName;
|
||||
} else if token.eq_ignore_ascii_case("FROM") {
|
||||
state = SelectParserState::TableName;
|
||||
} else {
|
||||
return Err(anyhow!("Expected comma or FROM keyword at or near '{}'", token));
|
||||
}
|
||||
}
|
||||
SelectParserState::TableName => {
|
||||
|
@ -237,7 +252,40 @@ impl Command {
|
|||
if token != ";" {
|
||||
return Err(anyhow!("Expected semicolon at or near '{}'", token));
|
||||
} else {
|
||||
return Ok(Command::Select(SelectCommand { table_name }));
|
||||
return Ok(Command::Select(SelectCommand { table_name, column_names }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Unexpected end of input"))
|
||||
}
|
||||
|
||||
|
||||
fn parse_delete_command(tokens: &mut Vec<String>) -> ::anyhow::Result<Command> {
|
||||
let mut state: DeleteParserState = DeleteParserState::FromKeyword;
|
||||
|
||||
// intermediate tmp vars
|
||||
let mut table_name = String::new();
|
||||
|
||||
while let Some(token) = &tokens.pop() {
|
||||
match state {
|
||||
DeleteParserState::FromKeyword => {
|
||||
if !token.eq_ignore_ascii_case("FROM") {
|
||||
return Err(anyhow!("Expected FROM keyword at or near '{}'", token));
|
||||
} else {
|
||||
state = DeleteParserState::TableName;
|
||||
}
|
||||
}
|
||||
DeleteParserState::TableName => {
|
||||
table_name = token.to_string();
|
||||
state = DeleteParserState::Semicolon;
|
||||
}
|
||||
DeleteParserState::Semicolon => {
|
||||
if token != ";" {
|
||||
return Err(anyhow!("Expected semicolon at or near '{}'", token));
|
||||
} else {
|
||||
return Ok(Command::Delete(DeleteCommand { table_name }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,6 +393,7 @@ impl Command {
|
|||
"CREATE" => Self::parse_create_command(&mut tokens),
|
||||
"INSERT" => Self::parse_insert_command(&mut tokens),
|
||||
"SELECT" => Self::parse_select_command(&mut tokens),
|
||||
"DELETE" => Self::parse_delete_command(&mut tokens),
|
||||
_ => Err(anyhow!("Unknown command '{}'", token)),
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue