fix(mysql): handle export terminators and escaped quotes

This commit is contained in:
zipg authored and GitHub committed 2026-07-21 17:13:50 +08:00
1 parent 8a546be58d
commit a12f8d09b7
2 files changed
+60 -7

No files matched your search

+39 -2
View File
@@ -382,11 +382,11 @@ impl SqlStatementSplitter {
}
match ch {
'\'' if !self.in_double_quote && !self.in_backtick && self.previous != Some('\\') => {
'\'' if !self.in_double_quote && !self.in_backtick && !has_odd_trailing_backslashes(&self.buffer) => {
self.in_single_quote = !self.in_single_quote;
self.buffer.push(ch);
}
'"' if !self.in_single_quote && !self.in_backtick && self.previous != Some('\\') => {
'"' if !self.in_single_quote && !self.in_backtick && !has_odd_trailing_backslashes(&self.buffer) => {
self.in_double_quote = !self.in_double_quote;
self.buffer.push(ch);
}
@@ -522,6 +522,10 @@ impl SqlStatementSplitter {
}
}
fn has_odd_trailing_backslashes(sql: &str) -> bool {
sql.as_bytes().iter().rev().take_while(|byte| **byte == b'\\').count() % 2 == 1
}
pub fn split_sql_statements(sql: &str) -> Vec<String> {
split_sql_statements_with_options(sql, SqlParsingOptions::default())
}
@@ -2457,6 +2461,39 @@ mod tests {
);
}
#[test]
fn closes_mysql_string_after_even_trailing_backslashes() {
let sql = r#"CREATE TABLE paths (value varchar(100) COMMENT 'Windows path\\'); DROP TABLE paths;"#;
assert_eq!(
split_sql_statements_for_database(sql, DatabaseType::Mysql),
vec![r#"CREATE TABLE paths (value varchar(100) COMMENT 'Windows path\\')"#, "DROP TABLE paths"]
);
}
#[test]
fn keeps_mysql_string_open_after_odd_trailing_backslashes() {
let sql = r#"INSERT INTO notes VALUES ('it\'s; still one value'); SELECT 1;"#;
assert_eq!(
split_sql_statements_for_database(sql, DatabaseType::Mysql),
vec![r#"INSERT INTO notes VALUES ('it\'s; still one value')"#, "SELECT 1"]
);
}
#[test]
fn closes_mysql_string_after_even_trailing_backslashes_across_chunks() {
let mut splitter =
SqlStatementSplitter::with_options(SqlParsingOptions::for_database_type(DatabaseType::Mysql));
assert!(splitter.push_chunk("CREATE TABLE paths (value varchar(100) COMMENT 'Windows path\\").is_empty());
assert_eq!(
splitter.push_chunk("\\'); DROP TABLE paths;"),
vec![r#"CREATE TABLE paths (value varchar(100) COMMENT 'Windows path\\')"#, "DROP TABLE paths"]
);
assert!(splitter.finish().is_empty());
}
#[test]
fn emits_trailing_statement_without_semicolon() {
assert_eq!(