From 5f1ca2050fce8a5a60b5bf2ee64b4092254fbf9a Mon Sep 17 00:00:00 2001 From: myyrakle Date: Tue, 19 Mar 2024 00:30:19 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[#69]=20lexer=20=ED=86=A0=ED=81=B0=EC=97=90?= =?UTF-8?q?=20TCL=20=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lexer/tokens.rs | 6 ++++++ 1 file changed, 6 insertions(+) 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), From ffd1fff4181a4076f97c769f7ba80825970d9ac8 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Tue, 19 Mar 2024 00:31:04 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[#69]=20lexer=20=ED=82=A4=EC=9B=8C=EB=93=9C?= =?UTF-8?q?=20=EB=A7=A4=ED=95=91=EC=97=90=20TCL=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lexer/tokenizer.rs | 4 ++++ 1 file changed, 4 insertions(+) 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), }; From 87917e4f2c2111d29e320a014c30edcd10f6c515 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Tue, 19 Mar 2024 00:50:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[#69]=20begin=20transaction=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/mod.rs | 7 ++++ src/ast/tcl/begin_transaction.rs | 8 ++++ src/parser/implements/mod.rs | 1 + src/parser/implements/tcl/begin.rs | 26 +++++++++++++ src/parser/implements/tcl/mod.rs | 1 + src/parser/parser.rs | 4 ++ src/parser/test/begin_transaction.rs | 55 ++++++++++++++++++++++++++++ src/parser/test/mod.rs | 2 + 8 files changed, 104 insertions(+) create mode 100644 src/parser/implements/tcl/begin.rs create mode 100644 src/parser/implements/tcl/mod.rs create mode 100644 src/parser/test/begin_transaction.rs 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/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/mod.rs b/src/parser/implements/tcl/mod.rs new file mode 100644 index 00000000..8d27fcce --- /dev/null +++ b/src/parser/implements/tcl/mod.rs @@ -0,0 +1 @@ +pub mod begin; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index def9ccfa..f432d2cd 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -86,6 +86,10 @@ 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); + } _ => { break; } diff --git a/src/parser/test/begin_transaction.rs b/src/parser/test/begin_transaction.rs new file mode 100644 index 00000000..0067accb --- /dev/null +++ b/src/parser/test/begin_transaction.rs @@ -0,0 +1,55 @@ +#![cfg(test)] + +use crate::{ + ast::{tcl::BeginTransactionQuery, 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); + } +} diff --git a/src/parser/test/mod.rs b/src/parser/test/mod.rs index c0f26644..7a0a3769 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 begin_transaction; From ea4e53d15f70e9aae77a1ee56f3ae94ec457b526 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Tue, 19 Mar 2024 00:53:41 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[#69]=20commit=20=ED=8C=8C=EC=8B=B1=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/tcl/commit.rs | 8 ++++ src/parser/implements/tcl/commit.rs | 14 +++++++ src/parser/implements/tcl/mod.rs | 1 + src/parser/parser.rs | 4 ++ src/parser/test/mod.rs | 2 +- .../test/{begin_transaction.rs => tcl.rs} | 40 ++++++++++++++++++- 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/parser/implements/tcl/commit.rs rename src/parser/test/{begin_transaction.rs => tcl.rs} (60%) 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/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 index 8d27fcce..bdfe18f0 100644 --- a/src/parser/implements/tcl/mod.rs +++ b/src/parser/implements/tcl/mod.rs @@ -1 +1,2 @@ pub mod begin; +pub mod commit; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index f432d2cd..3434bccc 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -90,6 +90,10 @@ impl Parser { let query = self.parse_begin_query(context.clone())?; statements.push(query); } + Token::Commit => { + let query = self.parse_commit_query(context.clone())?; + statements.push(query); + } _ => { break; } diff --git a/src/parser/test/mod.rs b/src/parser/test/mod.rs index 7a0a3769..3111f8f3 100644 --- a/src/parser/test/mod.rs +++ b/src/parser/test/mod.rs @@ -14,4 +14,4 @@ pub(crate) mod update; pub(crate) mod other; -pub(crate) mod begin_transaction; +pub(crate) mod tcl; diff --git a/src/parser/test/begin_transaction.rs b/src/parser/test/tcl.rs similarity index 60% rename from src/parser/test/begin_transaction.rs rename to src/parser/test/tcl.rs index 0067accb..23734b4e 100644 --- a/src/parser/test/begin_transaction.rs +++ b/src/parser/test/tcl.rs @@ -1,7 +1,10 @@ #![cfg(test)] use crate::{ - ast::{tcl::BeginTransactionQuery, SQLStatement}, + ast::{ + tcl::{BeginTransactionQuery, CommitQuery}, + SQLStatement, + }, parser::predule::{Parser, ParserContext}, }; @@ -53,3 +56,38 @@ pub fn begin_transaction() { 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); + } +} From a7e67b1b278939c81044bac6a580463463566b18 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Tue, 19 Mar 2024 00:55:36 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[#69]=20rollback=20=ED=8C=8C=EC=8B=B1=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/tcl/rollback.rs | 8 ++++++ src/parser/implements/tcl/mod.rs | 1 + src/parser/implements/tcl/rollback.rs | 14 ++++++++++ src/parser/parser.rs | 4 +++ src/parser/test/tcl.rs | 37 ++++++++++++++++++++++++++- 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/parser/implements/tcl/rollback.rs 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/parser/implements/tcl/mod.rs b/src/parser/implements/tcl/mod.rs index bdfe18f0..4e7894f7 100644 --- a/src/parser/implements/tcl/mod.rs +++ b/src/parser/implements/tcl/mod.rs @@ -1,2 +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 3434bccc..116fbbb2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -94,6 +94,10 @@ impl Parser { 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/tcl.rs b/src/parser/test/tcl.rs index 23734b4e..59a7bb43 100644 --- a/src/parser/test/tcl.rs +++ b/src/parser/test/tcl.rs @@ -2,7 +2,7 @@ use crate::{ ast::{ - tcl::{BeginTransactionQuery, CommitQuery}, + tcl::{BeginTransactionQuery, CommitQuery, RollbackQuery}, SQLStatement, }, parser::predule::{Parser, ParserContext}, @@ -91,3 +91,38 @@ pub fn commit() { 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); + } +}