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
88 changes: 87 additions & 1 deletion src/cli/ast/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::cli::{ast::{parser::Parser, WhereClause, Operator}, tokenizer::token::TokenTypes};
use crate::cli::{ast::{parser::Parser, WhereClause, Operator, OrderByClause, OrderByDirection, LimitClause}, tokenizer::token::TokenTypes};

use crate::db::table::Value;
use hex::decode;
Expand Down Expand Up @@ -55,6 +55,14 @@ pub fn tokens_to_identifier_list(parser: &mut Parser) -> Result<Vec<String>, Str
return Ok(identifiers);
}

pub fn get_table_name(parser: &mut Parser) -> Result<String, String> {
parser.advance()?;
let token = parser.current_token()?;
expect_token_type(parser, TokenTypes::Identifier)?;
let result = token.value.to_string();
Ok(result)
}

pub fn get_where_clause(parser: &mut Parser) -> Result<Option<WhereClause>, String> {
if expect_token_type(parser, TokenTypes::Where).is_err() {
return Ok(None);
Expand Down Expand Up @@ -86,4 +94,82 @@ pub fn get_where_clause(parser: &mut Parser) -> Result<Option<WhereClause>, Stri
operator: operator,
value: value,
}));
}


pub fn get_order_by(parser: &mut Parser) -> Result<Option<Vec<OrderByClause>>, String> {
if expect_token_type(parser, TokenTypes::Order).is_err() {
return Ok(None);
}
parser.advance()?;

expect_token_type(parser, TokenTypes::By)?;
parser.advance()?;

let mut order_by_clauses = vec![];
loop {
let token = parser.current_token()?;
expect_token_type(parser, TokenTypes::Identifier)?;
let column = token.value.to_string();
parser.advance()?;

let token = parser.current_token()?;
let direction = match token.token_type {
TokenTypes::Asc => {
parser.advance()?;
OrderByDirection::Asc
},
TokenTypes::Desc => {
parser.advance()?;
OrderByDirection::Desc
},
_ => OrderByDirection::Asc,
};

order_by_clauses.push(OrderByClause {
column: column,
direction: direction,
});

let token = parser.current_token()?;
if token.token_type != TokenTypes::Comma {
break;
}
parser.advance()?;
}
return Ok(Some(order_by_clauses));
}

pub fn get_limit(parser: &mut Parser) -> Result<Option<LimitClause>, String> {
if expect_token_type(parser, TokenTypes::Limit).is_err() {
return Ok(None);
}
parser.advance()?;

expect_token_type(parser, TokenTypes::IntLiteral)?;
let limit = token_to_value(parser)?;
parser.advance()?;

let token = parser.current_token()?;
if token.token_type != TokenTypes::Offset {
return Ok(Some(LimitClause {
limit: limit,
offset: None,
}));
}
parser.advance()?;

expect_token_type(parser, TokenTypes::IntLiteral)?;
let offset = token_to_value(parser)?;
if let Value::Integer(offset) = offset {
if offset < 0 {
return Err(parser.format_error());
}
};
parser.advance()?;

return Ok(Some(LimitClause {
limit: limit,
offset: Some(offset),
}));
}
106 changes: 106 additions & 0 deletions src/cli/ast/delete_statement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::cli::ast::{parser::Parser, SqlStatement, DeleteStatement, common::{expect_token_type, get_table_name, get_where_clause, get_order_by, get_limit}};
use crate::cli::tokenizer::token::TokenTypes;

pub fn build(parser: &mut Parser) -> Result<SqlStatement, String> {
parser.advance()?;
expect_token_type(parser, TokenTypes::From)?;
let table_name = get_table_name(parser)?;
parser.advance()?;
let where_clause = get_where_clause(parser)?;
let order_by_clause = get_order_by(parser)?;
let limit_clause = get_limit(parser)?;

return Ok(SqlStatement::DeleteStatement(DeleteStatement {
table_name: table_name,
where_clause: where_clause,
order_by_clause: order_by_clause,
limit_clause: limit_clause,
}));
}

#[cfg(test)]
mod tests {
use super::*;
use crate::cli::tokenizer::scanner::Token;
use crate::cli::ast::OrderByClause;
use crate::cli::ast::OrderByDirection;
use crate::cli::ast::LimitClause;
use crate::cli::ast::Operator;
use crate::cli::ast::WhereClause;
use crate::db::table::Value;

fn token(tt: TokenTypes, val: &'static str) -> Token<'static> {
Token {
token_type: tt,
value: val,
col_num: 0,
line_num: 1,
}
}

#[test]
fn delete_statement_with_all_tokens_is_generated_correctly() {
// DELETE FROM users;
let tokens = vec![
token(TokenTypes::Delete, "DELETE"),
token(TokenTypes::From, "FROM"),
token(TokenTypes::Identifier, "users"),
token(TokenTypes::SemiColon, ";"),
];
let mut parser = Parser::new(tokens);
let result = build(&mut parser);
assert!(result.is_ok());
let statement = result.unwrap();
let expected = SqlStatement::DeleteStatement(DeleteStatement {
table_name: "users".to_string(),
where_clause: None,
order_by_clause: None,
limit_clause: None,
});
assert_eq!(expected, statement);
}

#[test]
fn delete_statement_with_all_clauses_is_generated_correctly() {
// DELETE FROM users WHERE id = 1 ORDER BY id ASC LIMIT 10 OFFSET 5;
let tokens = vec![
token(TokenTypes::Delete, "DELETE"),
token(TokenTypes::From, "FROM"),
token(TokenTypes::Identifier, "users"),
token(TokenTypes::Where, "WHERE"),
token(TokenTypes::Identifier, "id"),
token(TokenTypes::Equals, "="),
token(TokenTypes::IntLiteral, "1"),
token(TokenTypes::Order, "ORDER"),
token(TokenTypes::By, "BY"),
token(TokenTypes::Identifier, "id"),
token(TokenTypes::Asc, "ASC"),
token(TokenTypes::Limit, "LIMIT"),
token(TokenTypes::IntLiteral, "10"),
token(TokenTypes::Offset, "OFFSET"),
token(TokenTypes::IntLiteral, "5"),
token(TokenTypes::SemiColon, ";"),
];
let mut parser = Parser::new(tokens);
let result = build(&mut parser);
assert!(result.is_ok());
let statement = result.unwrap();
let expected = SqlStatement::DeleteStatement(DeleteStatement {
table_name: "users".to_string(),
where_clause: Some(WhereClause {
column: "id".to_string(),
operator: Operator::Equals,
value: Value::Integer(1),
}),
order_by_clause: Some(vec![OrderByClause {
column: "id".to_string(),
direction: OrderByDirection::Asc,
}]),
limit_clause: Some(LimitClause {
limit: Value::Integer(10),
offset: Some(Value::Integer(5)),
}),
});
assert_eq!(expected, statement);
}
}
15 changes: 15 additions & 0 deletions src/cli/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ mod insert_statement;
mod parser;
mod select_statement;
mod update_statement;
mod delete_statement;

#[derive(Debug, PartialEq)]
pub enum SqlStatement {
CreateTable(CreateTableStatement),
InsertInto(InsertIntoStatement),
Select(SelectStatement),
UpdateStatement(UpdateStatement),
DeleteStatement(DeleteStatement),
}

#[derive(Debug, PartialEq)]
Expand All @@ -38,6 +40,14 @@ pub struct SelectStatement {
pub limit_clause: Option<LimitClause>,
}

#[derive(Debug, PartialEq)]
pub struct DeleteStatement {
pub table_name: String,
pub where_clause: Option<WhereClause>,
pub order_by_clause: Option<Vec<OrderByClause>>,
pub limit_clause: Option<LimitClause>,
}

#[derive(Debug, PartialEq)]
pub struct UpdateStatement {
pub table_name: String,
Expand Down Expand Up @@ -106,6 +116,7 @@ pub trait StatementBuilder {
fn build_insert(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String>;
fn build_select(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String>;
fn build_update(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String>;
fn build_delete(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String>;
}

pub struct DefaultStatementBuilder;
Expand All @@ -126,6 +137,10 @@ impl StatementBuilder for DefaultStatementBuilder {
fn build_update(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String> {
update_statement::build(parser)
}

fn build_delete(&self, parser: &mut parser::Parser) -> Result<SqlStatement, String> {
delete_statement::build(parser)
}
}

pub fn generate(tokens: Vec<Token>) -> Vec<Result<SqlStatement, String>> {
Expand Down
5 changes: 5 additions & 0 deletions src/cli/ast/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl<'a> Parser<'a> {
TokenTypes::Insert => Some(builder.build_insert(self)),
TokenTypes::Select => Some(builder.build_select(self)),
TokenTypes::Update => Some(builder.build_update(self)),
TokenTypes::Delete => Some(builder.build_delete(self)),
TokenTypes::EOF => None,
_ => {
Some(Err(self.format_error()))
Expand Down Expand Up @@ -142,6 +143,10 @@ mod tests {
fn build_update(&self, _parser: &mut Parser) -> Result<SqlStatement, String> {
todo!();
}

fn build_delete(&self, _parser: &mut Parser) -> Result<SqlStatement, String> {
todo!();
}
}

#[test]
Expand Down
95 changes: 5 additions & 90 deletions src/cli/ast/select_statement.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{cli::{ast::{common::{expect_token_type, get_where_clause, token_to_value, tokens_to_identifier_list}, parser::Parser, LimitClause, OrderByClause, OrderByDirection, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}, db::table::Value};
use crate::{cli::{ast::{common::{expect_token_type, get_where_clause, tokens_to_identifier_list, get_order_by, get_limit, get_table_name}, parser::Parser, SelectStatement, SelectStatementColumns, SqlStatement, WhereClause}, tokenizer::token::TokenTypes}};

pub fn build(parser: &mut Parser) -> Result<SqlStatement, String> {
parser.advance()?;
let columns = get_columns(parser)?;
let table_name = get_table_name(parser)?;
parser.advance()?;
let where_clause: Option<WhereClause> = get_where_clause(parser)?;
let order_by_clause = get_order_by(parser)?;
let limit_clause = get_limit(parser)?;
Expand Down Expand Up @@ -31,101 +32,15 @@ fn get_columns(parser: &mut Parser) -> Result<SelectStatementColumns, String> {
}
}

fn get_table_name(parser: &mut Parser) -> Result<String, String> {
parser.advance()?;
let token = parser.current_token()?;
expect_token_type(parser, TokenTypes::Identifier)?;

let result = token.value.to_string();
parser.advance()?;
Ok(result)
}

fn get_order_by(parser: &mut Parser) -> Result<Option<Vec<OrderByClause>>, String> {
if expect_token_type(parser, TokenTypes::Order).is_err() {
return Ok(None);
}
parser.advance()?;

expect_token_type(parser, TokenTypes::By)?;
parser.advance()?;

let mut order_by_clauses = vec![];
loop {
let token = parser.current_token()?;
expect_token_type(parser, TokenTypes::Identifier)?;
let column = token.value.to_string();
parser.advance()?;

let token = parser.current_token()?;
let direction = match token.token_type {
TokenTypes::Asc => {
parser.advance()?;
OrderByDirection::Asc
},
TokenTypes::Desc => {
parser.advance()?;
OrderByDirection::Desc
},
_ => OrderByDirection::Asc,
};

order_by_clauses.push(OrderByClause {
column: column,
direction: direction,
});

let token = parser.current_token()?;
if token.token_type != TokenTypes::Comma {
break;
}
parser.advance()?;
}
return Ok(Some(order_by_clauses));
}

fn get_limit(parser: &mut Parser) -> Result<Option<LimitClause>, String> {
if expect_token_type(parser, TokenTypes::Limit).is_err() {
return Ok(None);
}
parser.advance()?;

expect_token_type(parser, TokenTypes::IntLiteral)?;
let limit = token_to_value(parser)?;
parser.advance()?;

let token = parser.current_token()?;
if token.token_type != TokenTypes::Offset {
return Ok(Some(LimitClause {
limit: limit,
offset: None,
}));
}
parser.advance()?;

expect_token_type(parser, TokenTypes::IntLiteral)?;
let offset = token_to_value(parser)?;
if let Value::Integer(offset) = offset {
if offset < 0 {
return Err(parser.format_error());
}
};
parser.advance()?;

return Ok(Some(LimitClause {
limit: limit,
offset: Some(offset),
}));


}

#[cfg(test)]
mod tests {
use super::*;
use crate::cli::ast::Operator;
use crate::cli::tokenizer::scanner::Token;
use crate::db::table::Value;
use crate::cli::ast::OrderByClause;
use crate::cli::ast::OrderByDirection;
use crate::cli::ast::LimitClause;

fn token(tt: TokenTypes, val: &'static str) -> Token<'static> {
Token {
Expand Down
Loading
Loading