diff --git a/crates/dbx-core/src/db/mysql.rs b/crates/dbx-core/src/db/mysql.rs index 7e5e5989a3..428959072c 100644 --- a/crates/dbx-core/src/db/mysql.rs +++ b/crates/dbx-core/src/db/mysql.rs @@ -1468,7 +1468,11 @@ fn mysql_top_level_limit(sql: &str) -> Option { if depth == 0 && mysql_keyword_at(sql, i, "LIMIT") { return parse_mysql_limit_value(sql, i + "LIMIT".len()); } + // Move to next byte, but ensure we stay on a UTF-8 boundary i += 1; + while i < bytes.len() && !sql.is_char_boundary(i) { + i += 1; + } } None @@ -1497,13 +1501,20 @@ fn parse_usize_token(sql: &str, i: &mut usize) -> Option { if *i == start { return None; } - sql[start..*i].parse().ok() + // Ensure the slice is valid UTF-8 before parsing + std::str::from_utf8(&bytes[start..*i]).ok()?.parse().ok() } fn mysql_keyword_at(sql: &str, i: usize, keyword: &str) -> bool { let end = i + keyword.len(); - end <= sql.len() - && sql[i..end].eq_ignore_ascii_case(keyword) + if end > sql.len() { + return false; + } + // Ensure indices are on UTF-8 boundaries before slicing + if !sql.is_char_boundary(i) || !sql.is_char_boundary(end) { + return false; + } + sql[i..end].eq_ignore_ascii_case(keyword) && (i == 0 || !is_mysql_identifier_byte(sql.as_bytes()[i - 1])) && (end == sql.len() || !is_mysql_identifier_byte(sql.as_bytes()[end])) }