From ea6ea5d055c787c1c246258a8df3ae517e2c2ee2 Mon Sep 17 00:00:00 2001 From: slopgobblercobbler Date: Sat, 31 Oct 2026 20:12:44 +0100 Subject: [PATCH] MySQL: Fix FLUSH TABLES consuming semicolon delimiter In `parse_flush`, parsing `FLUSH TABLES` consumed the next token inside a loop using `self.next_token()`. When breaking out of the loop at the end of the table/options list, the following token (such as a semicolon delimiter) was already consumed and not restored, causing subsequent statements to fail parsing with `Expected: end of statement, found: `. Use `self.peek_token()` instead of unconditionally consuming tokens in the `TABLES` loop, advancing only on matched options and allowing delimiters and EOF to be preserved for statement separation. Closes #2764. --- src/parser/mod.rs | 7 ++++--- tests/sqlparser_mysql.rs | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 11accc3996..361d7741cc 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1097,16 +1097,17 @@ impl<'a> Parser<'a> { FlushType::Logs } else if self.parse_keywords(&[Keyword::TABLES]) { loop { - let next_token = self.next_token(); + let next_token = self.peek_token(); match &next_token.token { Token::Word(w) if w.keyword == Keyword::WITH => { + self.advance_token(); read_lock = self.parse_keywords(&[Keyword::READ, Keyword::LOCK]); } Token::Word(w) if w.keyword == Keyword::FOR => { + self.advance_token(); export = self.parse_keyword(Keyword::EXPORT); } - Token::Word(_) => { - self.prev_token(); + Token::Word(_) if tables.is_empty() && !read_lock && !export => { tables = self.parse_comma_separated(|p| p.parse_object_name(false))?; } _ => break, diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index d5ba2a9056..1139e9a16c 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -250,6 +250,26 @@ fn parse_flush_preserves_keyword_table_names() { } } +#[test] +fn parse_flush_tables_followed_by_statement() { + for sql in [ + "FLUSH TABLES; SELECT 1", + "FLUSH TABLES t1, t2; SELECT 1", + "FLUSH TABLES WITH READ LOCK; SELECT 1", + "FLUSH TABLES t1, t2 WITH READ LOCK; SELECT 1", + "FLUSH TABLES t1, t2 FOR EXPORT; SELECT 1", + ] { + let stmts = mysql_and_generic().parse_sql_statements(sql).unwrap(); + assert_eq!( + stmts.len(), + 2, + "Failed to parse multiple statements for: {sql}" + ); + assert!(matches!(stmts[0], Statement::Flush { .. })); + assert!(matches!(stmts[1], Statement::Query { .. })); + } +} + #[test] fn parse_show_columns() { assert_eq!(