From 2e7de4ccd6e747bf3b3ef843aa83f7fc8fdffba0 Mon Sep 17 00:00:00 2001 From: TcMits Date: Sat, 2 Aug 2025 15:10:41 +0700 Subject: [PATCH] parse SAVEPOINT, RELEASE --- core/benches/parser_benchmark.rs | 2 + core/parser/parser.rs | 75 ++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/core/benches/parser_benchmark.rs b/core/benches/parser_benchmark.rs index 6df67ca3a..aad5ebdc6 100644 --- a/core/benches/parser_benchmark.rs +++ b/core/benches/parser_benchmark.rs @@ -51,6 +51,8 @@ fn bench_parser(criterion: &mut Criterion) { "COMMIT TRANSACTION my_trans", "ROLLBACK", "ROLLBACK TRANSACTION my_transaction TO my_savepoint", + "SAVEPOINT my_savepoint", + "RELEASE SAVEPOINT my_savepoint", ]; for query in queries.iter() { diff --git a/core/parser/parser.rs b/core/parser/parser.rs index 16ed4af24..fbddedfd2 100644 --- a/core/parser/parser.rs +++ b/core/parser/parser.rs @@ -220,6 +220,8 @@ impl<'a> Parser<'a> { TokenType::TK_COMMIT, TokenType::TK_END, TokenType::TK_ROLLBACK, + TokenType::TK_SAVEPOINT, + TokenType::TK_RELEASE, // add more ])?; @@ -227,6 +229,8 @@ impl<'a> Parser<'a> { TokenType::TK_BEGIN => self.parse_begin(), TokenType::TK_COMMIT | TokenType::TK_END => self.parse_commit(), TokenType::TK_ROLLBACK => self.parse_rollback(), + TokenType::TK_SAVEPOINT => self.parse_savepoint(), + TokenType::TK_RELEASE => self.parse_release(), _ => unreachable!(), } } @@ -242,7 +246,7 @@ impl<'a> Parser<'a> { } #[inline(always)] - fn parse_nm(&mut self) -> Result { + fn parse_nm(&mut self) -> Name { let tok = self.eat_assert(&[ TokenType::TK_ID, TokenType::TK_STRING, @@ -252,8 +256,8 @@ impl<'a> Parser<'a> { let first_char = tok.value[0]; // no need to check empty match first_char { - b'[' | b'\'' | b'`' | b'"' => Ok(Name::Quoted(from_bytes(tok.value))), - _ => Ok(Name::Ident(from_bytes(tok.value))), + b'[' | b'\'' | b'`' | b'"' => Name::Quoted(from_bytes(tok.value)), + _ => Name::Ident(from_bytes(tok.value)), } } @@ -265,7 +269,7 @@ impl<'a> Parser<'a> { TokenType::TK_TRANSACTION => { self.eat_assert(&[TokenType::TK_TRANSACTION]); if self.peek_nm().ok().is_some() { - Ok(self.parse_nm().ok()) + Ok(Some(self.parse_nm())) } else { Ok(None) } @@ -324,14 +328,12 @@ impl<'a> Parser<'a> { if tok.token_type == Some(TokenType::TK_TO) { self.eat_assert(&[TokenType::TK_TO]); - if let Some(tok) = self.peek_ignore_eof()? { - if tok.token_type == Some(TokenType::TK_SAVEPOINT) { - self.eat_assert(&[TokenType::TK_SAVEPOINT]); - } + if self.peek_no_eof()?.token_type == Some(TokenType::TK_SAVEPOINT) { + self.eat_assert(&[TokenType::TK_SAVEPOINT]); } self.peek_nm()?; - self.parse_nm().ok() + Some(self.parse_nm()) } else { None } @@ -343,6 +345,29 @@ impl<'a> Parser<'a> { savepoint_name, }) } + + #[inline(always)] + fn parse_savepoint(&mut self) -> Result { + self.eat_assert(&[TokenType::TK_SAVEPOINT]); + self.peek_nm()?; + Ok(Stmt::Savepoint { + name: self.parse_nm(), + }) + } + + #[inline(always)] + fn parse_release(&mut self) -> Result { + self.eat_assert(&[TokenType::TK_RELEASE]); + + if self.peek_no_eof()?.token_type == Some(TokenType::TK_SAVEPOINT) { + self.eat_assert(&[TokenType::TK_SAVEPOINT]); + } + + self.peek_nm()?; + Ok(Stmt::Release { + name: self.parse_nm(), + }) + } } #[cfg(test)] @@ -512,6 +537,38 @@ mod tests { savepoint_name: Some(Name::Ident("my_savepoint".to_string())), })], ), + // savepoint + ( + b"SAVEPOINT my_savepoint".as_slice(), + vec![Cmd::Stmt(Stmt::Savepoint { + name: Name::Ident("my_savepoint".to_string()), + })], + ), + ( + b"SAVEPOINT 'my_savepoint'".as_slice(), + vec![Cmd::Stmt(Stmt::Savepoint { + name: Name::Quoted("'my_savepoint'".to_string()), + })], + ), + // release + ( + b"RELEASE my_savepoint".as_slice(), + vec![Cmd::Stmt(Stmt::Release { + name: Name::Ident("my_savepoint".to_string()), + })], + ), + ( + b"RELEASE SAVEPOINT my_savepoint".as_slice(), + vec![Cmd::Stmt(Stmt::Release { + name: Name::Ident("my_savepoint".to_string()), + })], + ), + ( + b"RELEASE SAVEPOINT 'my_savepoint'".as_slice(), + vec![Cmd::Stmt(Stmt::Release { + name: Name::Quoted("'my_savepoint'".to_string()), + })], + ), ]; for (input, expected) in test_cases {