diff --git a/src/ast/mod.rs b/src/ast/mod.rs index b1c95973..c44e153c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -29,6 +29,13 @@ pub enum SQLStatement { DCL(DCLStatement), TCL(TCLStatement), Other(OtherStatement), + None, +} + +impl Default for SQLStatement { + fn default() -> Self { + SQLStatement::None + } } #[derive(Clone, Debug, PartialEq)] diff --git a/src/ast/tcl/begin_transaction.rs b/src/ast/tcl/begin_transaction.rs index 3ac7c204..04e5f698 100644 --- a/src/ast/tcl/begin_transaction.rs +++ b/src/ast/tcl/begin_transaction.rs @@ -1,2 +1,10 @@ +use crate::ast::{SQLStatement, TCLStatement}; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct BeginTransactionQuery {} + +impl From for SQLStatement { + fn from(value: BeginTransactionQuery) -> SQLStatement { + SQLStatement::TCL(TCLStatement::BeginTransaction(value)) + } +} diff --git a/src/ast/tcl/commit.rs b/src/ast/tcl/commit.rs index e7186dc1..281ea422 100644 --- a/src/ast/tcl/commit.rs +++ b/src/ast/tcl/commit.rs @@ -1,2 +1,10 @@ +use crate::ast::{SQLStatement, TCLStatement}; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct CommitQuery {} + +impl From for SQLStatement { + fn from(value: CommitQuery) -> SQLStatement { + SQLStatement::TCL(TCLStatement::Commit(value)) + } +} diff --git a/src/ast/tcl/rollback.rs b/src/ast/tcl/rollback.rs index 28639340..91d2d841 100644 --- a/src/ast/tcl/rollback.rs +++ b/src/ast/tcl/rollback.rs @@ -1,2 +1,10 @@ +use crate::ast::{SQLStatement, TCLStatement}; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct RollbackQuery {} + +impl From for SQLStatement { + fn from(value: RollbackQuery) -> SQLStatement { + SQLStatement::TCL(TCLStatement::Rollback(value)) + } +} diff --git a/src/lexer/tokenizer.rs b/src/lexer/tokenizer.rs index 819191fa..b406f219 100644 --- a/src/lexer/tokenizer.rs +++ b/src/lexer/tokenizer.rs @@ -173,6 +173,10 @@ impl Tokenizer { "NULLS" => Token::Nulls, "FIRST" => Token::First, "LAST" => Token::Last, + "BEGIN" => Token::Begin, + "TRANSACTION" => Token::Transaction, + "COMMIT" => Token::Commit, + "ROLLBACK" => Token::Rollback, _ => Token::Identifier(identifier), }; diff --git a/src/lexer/tokens.rs b/src/lexer/tokens.rs index d2e3ab7d..b8422ceb 100644 --- a/src/lexer/tokens.rs +++ b/src/lexer/tokens.rs @@ -64,6 +64,12 @@ pub enum Token { Default, Data, + // TCL + Begin, + Transaction, + Commit, + Rollback, + // ETC // Analyze, CodeComment(String), diff --git a/src/parser/implements/mod.rs b/src/parser/implements/mod.rs index f679d9a5..12d69c65 100644 --- a/src/parser/implements/mod.rs +++ b/src/parser/implements/mod.rs @@ -3,4 +3,5 @@ pub mod ddl; pub mod debug; pub mod dml; pub mod other; +pub mod tcl; pub mod utils; diff --git a/src/parser/implements/tcl/begin.rs b/src/parser/implements/tcl/begin.rs new file mode 100644 index 00000000..0914a852 --- /dev/null +++ b/src/parser/implements/tcl/begin.rs @@ -0,0 +1,26 @@ +use std::error::Error; + +use crate::ast::tcl::BeginTransactionQuery; +use crate::ast::SQLStatement; +use crate::errors::predule::ParsingError; +use crate::lexer::tokens::Token; +use crate::parser::predule::{Parser, ParserContext}; + +impl Parser { + pub(crate) fn parse_begin_query( + &mut self, + _context: ParserContext, + ) -> Result> { + if !self.has_next_token() { + return Err(ParsingError::boxed("E2001 need more tokens")); + } + + let current_token = self.get_next_token(); + + if current_token != Token::Transaction { + return Err(ParsingError::boxed("E2002 Expected BEGIN")); + } + + Ok(BeginTransactionQuery {}.into()) + } +} diff --git a/src/parser/implements/tcl/commit.rs b/src/parser/implements/tcl/commit.rs new file mode 100644 index 00000000..e821578d --- /dev/null +++ b/src/parser/implements/tcl/commit.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use crate::ast::tcl::CommitQuery; +use crate::ast::SQLStatement; +use crate::parser::predule::{Parser, ParserContext}; + +impl Parser { + pub(crate) fn parse_commit_query( + &mut self, + _context: ParserContext, + ) -> Result> { + Ok(CommitQuery {}.into()) + } +} diff --git a/src/parser/implements/tcl/mod.rs b/src/parser/implements/tcl/mod.rs new file mode 100644 index 00000000..4e7894f7 --- /dev/null +++ b/src/parser/implements/tcl/mod.rs @@ -0,0 +1,3 @@ +pub mod begin; +pub mod commit; +pub mod rollback; diff --git a/src/parser/implements/tcl/rollback.rs b/src/parser/implements/tcl/rollback.rs new file mode 100644 index 00000000..dcec139a --- /dev/null +++ b/src/parser/implements/tcl/rollback.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use crate::ast::tcl::RollbackQuery; +use crate::ast::SQLStatement; +use crate::parser::predule::{Parser, ParserContext}; + +impl Parser { + pub(crate) fn parse_rollback_query( + &mut self, + _context: ParserContext, + ) -> Result> { + Ok(RollbackQuery {}.into()) + } +} diff --git a/src/parser/parser.rs b/src/parser/parser.rs index def9ccfa..116fbbb2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -86,6 +86,18 @@ impl Parser { let query = self.parse_desc_query(context.clone())?; statements.push(query); } + Token::Begin => { + let query = self.parse_begin_query(context.clone())?; + statements.push(query); + } + Token::Commit => { + let query = self.parse_commit_query(context.clone())?; + statements.push(query); + } + Token::Rollback => { + let query = self.parse_rollback_query(context.clone())?; + statements.push(query); + } _ => { break; } diff --git a/src/parser/test/mod.rs b/src/parser/test/mod.rs index c0f26644..3111f8f3 100644 --- a/src/parser/test/mod.rs +++ b/src/parser/test/mod.rs @@ -13,3 +13,5 @@ pub(crate) mod select; pub(crate) mod update; pub(crate) mod other; + +pub(crate) mod tcl; diff --git a/src/parser/test/tcl.rs b/src/parser/test/tcl.rs new file mode 100644 index 00000000..59a7bb43 --- /dev/null +++ b/src/parser/test/tcl.rs @@ -0,0 +1,128 @@ +#![cfg(test)] + +use crate::{ + ast::{ + tcl::{BeginTransactionQuery, CommitQuery, RollbackQuery}, + SQLStatement, + }, + parser::predule::{Parser, ParserContext}, +}; + +#[test] +pub fn begin_transaction() { + struct TestCase { + name: String, + input: String, + expected: SQLStatement, + want_err: bool, + } + + let test_cases = vec![ + TestCase { + name: "정상적인 트랜잭션 시작".to_owned(), + input: "BEGIN TRANSACTION;".to_owned(), + expected: BeginTransactionQuery {}.into(), + want_err: false, + }, + TestCase { + name: "begin만 있는 경우".to_owned(), + input: "BEGIN;".to_owned(), + expected: Default::default(), + want_err: true, + }, + TestCase { + name: "begin 이후에 기대하지 않은 입력이 있는 경우".to_owned(), + input: "BEGIN TRANSITION;".to_owned(), + expected: Default::default(), + want_err: true, + }, + ]; + + for tc in test_cases { + let mut parser = Parser::new(tc.input).unwrap(); + + let result = parser.parse(ParserContext::default()); + + if tc.want_err { + assert!( + result.is_err(), + "{} - expected error, got {:?}", + tc.name, + result + ); + continue; + } + + assert_eq!(result.unwrap(), vec![tc.expected], "{}", tc.name); + } +} + +#[test] +pub fn commit() { + struct TestCase { + name: String, + input: String, + expected: SQLStatement, + want_err: bool, + } + + let test_cases = vec![TestCase { + name: "정상적인 Commit 명령".to_owned(), + input: "COMMIT;".to_owned(), + expected: CommitQuery {}.into(), + want_err: false, + }]; + + for tc in test_cases { + let mut parser = Parser::new(tc.input).unwrap(); + + let result = parser.parse(ParserContext::default()); + + if tc.want_err { + assert!( + result.is_err(), + "{} - expected error, got {:?}", + tc.name, + result + ); + continue; + } + + assert_eq!(result.unwrap(), vec![tc.expected], "{}", tc.name); + } +} + +#[test] +pub fn rollback() { + struct TestCase { + name: String, + input: String, + expected: SQLStatement, + want_err: bool, + } + + let test_cases = vec![TestCase { + name: "정상적인 ROLLBACK 명령".to_owned(), + input: "ROLLBACK;".to_owned(), + expected: RollbackQuery {}.into(), + want_err: false, + }]; + + for tc in test_cases { + let mut parser = Parser::new(tc.input).unwrap(); + + let result = parser.parse(ParserContext::default()); + + if tc.want_err { + assert!( + result.is_err(), + "{} - expected error, got {:?}", + tc.name, + result + ); + continue; + } + + assert_eq!(result.unwrap(), vec![tc.expected], "{}", tc.name); + } +}