Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions src/cli/ast/insert_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,28 @@ fn into_statement(parser: &mut Parser) -> Result<SqlStatement, String> {
}
}

return Ok(InsertInto(InsertIntoStatement {
let statement = InsertIntoStatement {
table_name: table_name,
columns: columns,
values: values,
}));
};
validate_insert_statement(&statement)?;
return Ok(InsertInto(statement));
}

fn validate_insert_statement(statement: &InsertIntoStatement) -> Result<(), String> {
for row in &statement.values {
if row.len() != statement.values[0].len() {
return Err(format!("Rows have different lengths"));
}
}

if let Some(columns) = &statement.columns {
if columns.len() != statement.values[0].len() {
return Err(format!("Columns and values have different lengths"));
}
}
return Ok(());
}

fn get_values(parser: &mut Parser) -> Result<Vec<Value>, String> {
Expand Down Expand Up @@ -279,4 +296,63 @@ mod tests {
let result = build(&mut parser);
assert!(result.is_err());
}

#[test]
fn insert_with_different_lengths_is_error() {
// INSERT INTO users VALUES (1, "Alice"), (2, "Bob", "Charlie");
let tokens = vec![
token(TokenTypes::Insert, "INSERT"),
token(TokenTypes::Into, "INTO"),
token(TokenTypes::Identifier, "users"),
token(TokenTypes::Values, "VALUES"),
token(TokenTypes::LeftParen, "("),
token(TokenTypes::IntLiteral, "1"),
token(TokenTypes::Comma, ","),
token(TokenTypes::String, "Alice"),
token(TokenTypes::RightParen, ")"),
token(TokenTypes::Comma, ","),
token(TokenTypes::LeftParen, "("),
token(TokenTypes::IntLiteral, "2"),
token(TokenTypes::Comma, ","),
token(TokenTypes::String, "Bob"),
token(TokenTypes::Comma, ","),
token(TokenTypes::String, "Charlie"),
token(TokenTypes::RightParen, ")"),
token(TokenTypes::SemiColon, ";"),
];
let mut parser = Parser::new(tokens);
let result = build(&mut parser);
assert!(result.is_err());
let expected = Err("Rows have different lengths".to_string());
assert_eq!(expected, result);
}

#[test]
fn insert_with_different_column_and_value_lengths_is_error() {
// INSERT INTO users (id, name) VALUES (1, "Alice", "Bob");
let tokens = vec![
token(TokenTypes::Insert, "INSERT"),
token(TokenTypes::Into, "INTO"),
token(TokenTypes::Identifier, "users"),
token(TokenTypes::LeftParen, "("),
token(TokenTypes::Identifier, "id"),
token(TokenTypes::Comma, ","),
token(TokenTypes::Identifier, "name"),
token(TokenTypes::RightParen, ")"),
token(TokenTypes::Values, "VALUES"),
token(TokenTypes::LeftParen, "("),
token(TokenTypes::IntLiteral, "1"),
token(TokenTypes::Comma, ","),
token(TokenTypes::String, "Alice"),
token(TokenTypes::Comma, ","),
token(TokenTypes::String, "Bob"),
token(TokenTypes::RightParen, ")"),
token(TokenTypes::SemiColon, ";"),
];
let mut parser = Parser::new(tokens);
let result = build(&mut parser);
assert!(result.is_err());
let expected = Err("Columns and values have different lengths".to_string());
assert_eq!(expected, result);
}
}
9 changes: 9 additions & 0 deletions src/cli/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ pub enum SelectStatementColumns {
Specific(Vec<String>),
}

impl SelectStatementColumns {
pub fn columns(&self) -> Result<&Vec<String>, String> {
return match self {
SelectStatementColumns::All => Err("Cannot get columns from all columns".to_string()),
SelectStatementColumns::Specific(columns) => Ok(columns),
}
}
}

#[derive(Debug, PartialEq)]
pub enum Operator {
Equals,
Expand Down
78 changes: 74 additions & 4 deletions src/db/database.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::db::table::{Table, Value};
use crate::cli::ast::{SqlStatement, CreateTableStatement, InsertIntoStatement, SelectStatement};
use crate::db::table::select;
use crate::db::table::insert;
use std::collections::HashMap;

pub struct Database {
Expand Down Expand Up @@ -34,20 +36,20 @@ impl Database {
if self.has_table(&statement.table_name) {
return Err(format!("Table {} already exists", statement.table_name));
}
let table_name = statement.table_name;
self.tables.insert(table_name.clone(), Table::new(table_name, statement.columns));
let table = Table::new(statement.table_name, statement.columns) ;
self.tables.insert(table.name.clone(), table);
Ok(())
}

fn insert_into_table(&mut self, statement: InsertIntoStatement) -> Result<(), String> {
let table = self.get_table_mut(&statement.table_name)?;
table.insert(statement)?;
insert::insert(table, statement)?;
Ok(())
}

fn select_from_table(&mut self, statement: SelectStatement) -> Result<Vec<Vec<Value>>, String> {
let table = self.get_table(&statement.table_name)?;
let rows = table.select(statement)?;
let rows = select::select(table, statement)?;
Ok(rows)
}

Expand All @@ -68,4 +70,72 @@ impl Database {
}
Ok(self.tables.get_mut(table_name).unwrap())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::cli::ast::CreateTableStatement;
use crate::db::table::{ColumnDefinition, DataType};


fn default_database() -> Database {
Database {
tables: HashMap::from([
("users".to_string(), Table::new("users".to_string(), vec![
ColumnDefinition {
name: "id".to_string(),
data_type: DataType::Integer,
constraints: vec![]
},
ColumnDefinition {
name: "name".to_string(),
data_type: DataType::Text,
constraints: vec![]
},
]))
])
}
}

#[test]
fn create_table_generates_proper_table() {
let statement = CreateTableStatement {
table_name: "users".to_string(),
columns: vec![
ColumnDefinition {
name: "id".to_string(),
data_type: DataType::Integer,
constraints: vec![]
},
],
};
let mut database = Database::new();
assert!(database.create_table(statement).is_ok());
assert!(database.has_table("users"));
}

#[test]
fn has_table_returns_proper_response() {
let database = default_database();
assert!(database.has_table("users"));
assert!(!database.has_table("not_users"));
}

#[test]
fn get_table_funcs_returns_proper_table() {
let mut database = default_database();
let table = database.get_table("users");
assert!(table.is_ok());
assert_eq!(table.unwrap().name, "users");
let table = database.get_table("not_users");
assert!(table.is_err());
assert_eq!(table.unwrap_err(), "Table not_users does not exist");
let table = database.get_table_mut("users");
assert!(table.is_ok());
assert_eq!(table.unwrap().name, "users");
let table = database.get_table_mut("not_users");
assert!(table.is_err());
assert_eq!(table.unwrap_err(), "Table not_users does not exist");
}
}
2 changes: 1 addition & 1 deletion src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod database;
pub mod table;
pub mod table;
Loading
Loading