Skip to content

Commit

Permalink
support alter and drop statements
Browse files Browse the repository at this point in the history
  • Loading branch information
sachaarbonel committed Jan 7, 2025
1 parent 7962e30 commit 6b51960
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 3 deletions.
118 changes: 117 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use sql::{
data_value::DataValue,
statements::{
create::CreateStatement, delete::DeleteStatement, insert::InsertStatement,
select::SelectStatement, update::UpdateStatement, Statement,
select::SelectStatement, update::UpdateStatement, alter::{AlterStatement, AlterType},
drop::DropStatement, Statement,
},
};

Expand Down Expand Up @@ -372,6 +373,63 @@ impl<S: Storage, FTS: Search> ReefDB<S, FTS> {
}
}
}
Statement::Alter(AlterStatement { table_name, alter_type }) => {
if let Some((schema, rows)) = self.tables.get_table(&table_name) {
match alter_type {
AlterType::AddColumn(column_def) => {
schema.push(column_def.clone());
for row in rows.iter_mut() {
row.push(DataValue::Text("NULL".to_string()));
}
if column_def.data_type == DataType::FTSText {
self.inverted_index.add_column(&table_name, &column_def.name);
}
Ok(ReefDBResult::AlterTable)
}
AlterType::DropColumn(column_name) => {
if let Some(col_idx) = schema.iter().position(|col| col.name == column_name) {
schema.remove(col_idx);
for row in rows.iter_mut() {
row.remove(col_idx);
}
Ok(ReefDBResult::AlterTable)
} else {
Err(ReefDBError::ColumnNotFound(column_name))
}
}
AlterType::RenameColumn(old_name, new_name) => {
if let Some(col) = schema.iter_mut().find(|col| col.name == old_name) {
col.name = new_name;
Ok(ReefDBResult::AlterTable)
} else {
Err(ReefDBError::ColumnNotFound(old_name))
}
}
}
} else {
Err(ReefDBError::TableNotFound(table_name))
}
}
Statement::Drop(DropStatement { table_name }) => {
if self.tables.table_exists(&table_name) {
// Get FTS columns before removing the table
let fts_columns = self.tables.get_fts_columns(&table_name);

// Remove the table
self.tables.remove_table(&table_name);

// Clean up FTS indexes for the table
for column in fts_columns {
// Note: We might want to add a method to remove all FTS entries for a table
// For now, we'll just remove document entries
self.inverted_index.remove_document(&table_name, &column, 0);
}

Ok(ReefDBResult::DropTable)
} else {
Err(ReefDBError::TableNotFound(table_name))
}
}
}
}
}
Expand Down Expand Up @@ -639,4 +697,62 @@ mod tests {
vec![DataValue::Text("bob".to_string()), DataValue::Integer(31)]
);
}

#[test]
fn test_alter_and_drop() {
let mut db = InMemoryReefDB::new();

let queries = vec![
"CREATE TABLE users (name TEXT, age INTEGER)",
"INSERT INTO users VALUES ('alice', 30)",
"ALTER TABLE users ADD COLUMN email TEXT",
"SELECT name, age, email FROM users",
"ALTER TABLE users RENAME COLUMN email TO contact",
"SELECT name, age, contact FROM users",
"ALTER TABLE users DROP COLUMN contact",
"SELECT name, age FROM users",
"DROP TABLE users",
"CREATE TABLE users (name TEXT)", // Should work as table was dropped
];

let mut results = Vec::new();
for query in queries {
results.push(db.query(query));
}

let expected_results = vec![
Ok(ReefDBResult::CreateTable),
Ok(ReefDBResult::Insert(1)),
Ok(ReefDBResult::AlterTable),
Ok(ReefDBResult::Select(vec![(
0,
vec![
DataValue::Text("alice".to_string()),
DataValue::Integer(30),
DataValue::Text("NULL".to_string()),
],
)])),
Ok(ReefDBResult::AlterTable),
Ok(ReefDBResult::Select(vec![(
0,
vec![
DataValue::Text("alice".to_string()),
DataValue::Integer(30),
DataValue::Text("NULL".to_string()),
],
)])),
Ok(ReefDBResult::AlterTable),
Ok(ReefDBResult::Select(vec![(
0,
vec![
DataValue::Text("alice".to_string()),
DataValue::Integer(30),
],
)])),
Ok(ReefDBResult::DropTable),
Ok(ReefDBResult::CreateTable),
];

assert_eq!(results, expected_results);
}
}
2 changes: 2 additions & 0 deletions src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ pub enum ReefDBResult {
CreateTable,
Update(usize),
Delete(usize),
AlterTable,
DropTable,
}
75 changes: 75 additions & 0 deletions src/sql/statements/alter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use nom::{
branch::alt,
bytes::complete::tag_no_case,
character::complete::{multispace1, alphanumeric1},
combinator::map,
sequence::tuple,
IResult,
};

use crate::sql::column_def::ColumnDef;
use super::Statement;

#[derive(Debug, PartialEq)]
pub enum AlterType {
AddColumn(ColumnDef),
DropColumn(String),
RenameColumn(String, String),
}

#[derive(Debug, PartialEq)]
pub struct AlterStatement {
pub table_name: String,
pub alter_type: AlterType,
}

impl AlterStatement {
pub fn parse(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag_no_case("ALTER TABLE")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = alphanumeric1(input)?;
let (input, _) = multispace1(input)?;

let (input, alter_type) = alt((
parse_add_column,
parse_drop_column,
parse_rename_column,
))(input)?;

Ok((
input,
Statement::Alter(AlterStatement {
table_name: table_name.to_string(),
alter_type,
}),
))
}
}

fn parse_add_column(input: &str) -> IResult<&str, AlterType> {
let (input, _) = tag_no_case("ADD COLUMN")(input)?;
let (input, _) = multispace1(input)?;
let (input, column_def) = ColumnDef::parse(input)?;

Ok((input, AlterType::AddColumn(column_def)))
}

fn parse_drop_column(input: &str) -> IResult<&str, AlterType> {
let (input, _) = tag_no_case("DROP COLUMN")(input)?;
let (input, _) = multispace1(input)?;
let (input, column_name) = alphanumeric1(input)?;

Ok((input, AlterType::DropColumn(column_name.to_string())))
}

fn parse_rename_column(input: &str) -> IResult<&str, AlterType> {
let (input, _) = tag_no_case("RENAME COLUMN")(input)?;
let (input, _) = multispace1(input)?;
let (input, old_name) = alphanumeric1(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag_no_case("TO")(input)?;
let (input, _) = multispace1(input)?;
let (input, new_name) = alphanumeric1(input)?;

Ok((input, AlterType::RenameColumn(old_name.to_string(), new_name.to_string())))
}
27 changes: 27 additions & 0 deletions src/sql/statements/drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use nom::{
bytes::complete::tag_no_case,
character::complete::{multispace1, alphanumeric1},
IResult,
};

use super::Statement;

#[derive(Debug, PartialEq)]
pub struct DropStatement {
pub table_name: String,
}

impl DropStatement {
pub fn parse(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag_no_case("DROP TABLE")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = alphanumeric1(input)?;

Ok((
input,
Statement::Drop(DropStatement {
table_name: table_name.to_string(),
}),
))
}
}
9 changes: 7 additions & 2 deletions src/sql/statements/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use self::{
create::CreateStatement, delete::DeleteStatement, insert::InsertStatement,
select::SelectStatement, update::UpdateStatement,
select::SelectStatement, update::UpdateStatement, alter::AlterStatement, drop::DropStatement,
};

use nom::{branch::alt, character::complete::multispace0, sequence::preceded, IResult};


pub mod create;
pub mod delete;
pub mod insert;
pub mod select;
pub mod update;
pub mod alter;
pub mod drop;

#[derive(Debug, PartialEq)]
pub enum Statement {
Expand All @@ -19,6 +20,8 @@ pub enum Statement {
Select(SelectStatement),
Update(UpdateStatement),
Delete(DeleteStatement),
Alter(AlterStatement),
Drop(DropStatement),
}

impl Statement {
Expand All @@ -31,6 +34,8 @@ impl Statement {
SelectStatement::parse,
UpdateStatement::parse,
DeleteStatement::parse,
AlterStatement::parse,
DropStatement::parse,
)),
)(input)
}
Expand Down
8 changes: 8 additions & 0 deletions src/storage/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,12 @@ impl Storage for OnDiskStorage {
fn get_table_ref(&self, table_name: &str) -> Option<&(Vec<ColumnDef>, Vec<Vec<DataValue>>)> {
self.tables.get(table_name)
}

fn remove_table(&mut self, table_name: &str) -> bool {
let removed = self.tables.remove(table_name).is_some();
if removed {
self.save();
}
removed
}
}
4 changes: 4 additions & 0 deletions src/storage/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ impl Storage for InMemoryStorage {
fn get_table_ref(&self, table_name: &str) -> Option<&(Vec<ColumnDef>, Vec<Vec<DataValue>>)> {
self.tables.get(table_name)
}

fn remove_table(&mut self, table_name: &str) -> bool {
self.tables.remove(table_name).is_some()
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ pub trait Storage {
fn get_schema_ref(&self, table_name: &str) -> Option<&Vec<ColumnDef>> {
self.get_table_ref(table_name).map(|(schema, _)| schema)
}

fn remove_table(&mut self, table_name: &str) -> bool;
}

0 comments on commit 6b51960

Please sign in to comment.