fix(mysql): respect query page size with existing limit

This commit is contained in:
t8y2 2026-07-09 21:44:28 +08:00
parent a9e679ec7e
commit ed9fe57617
1 changed files with 60 additions and 1 deletions

View File

@ -803,6 +803,33 @@ fn has_top_level_limit(sql: &str) -> bool {
top_level_sql_tokens(sql).iter().any(|token| token.text == "LIMIT")
}
fn top_level_limit_row_count(sql: &str) -> Option<usize> {
let token = top_level_sql_tokens(sql).into_iter().find(|token| token.text == "LIMIT")?;
parse_standard_limit_row_count(sql, token.start + token.text.len())
}
fn parse_standard_limit_row_count(sql: &str, start: usize) -> Option<usize> {
let mut cursor = skip_sql_whitespace(sql, start);
let first = parse_usize_literal(sql, &mut cursor)?;
cursor = skip_sql_whitespace(sql, cursor);
if sql.get(cursor..)?.starts_with(',') {
cursor = skip_sql_whitespace(sql, cursor + 1);
return parse_usize_literal(sql, &mut cursor);
}
Some(first)
}
fn parse_usize_literal(sql: &str, cursor: &mut usize) -> Option<usize> {
let start = *cursor;
while *cursor < sql.len() && sql.as_bytes()[*cursor].is_ascii_digit() {
*cursor += 1;
}
if *cursor == start {
return None;
}
sql[start..*cursor].parse().ok()
}
fn has_top_level_informix_row_limit(sql: &str) -> bool {
if has_top_level_limit(sql) {
return true;
@ -914,7 +941,9 @@ fn add_standard_limit(
// ordering across pages in distributed databases like Doris.
return add_outer_standard_limit(statement, database_type, limit, offset, &order_sql);
}
if offset > 0 {
// A user/top-level LIMIT can still be wider than the selected grid page size.
// Wrap it so the first page respects the UI page limit while preserving the user's cap.
if offset > 0 || top_level_limit_row_count(statement).is_some_and(|row_count| row_count > limit) {
return add_outer_standard_limit(statement, database_type, limit, offset, "");
}
return format!("{statement};");
@ -1911,6 +1940,36 @@ WHERE u.id = picked.id;
assert_eq!(result.sql.unwrap(), "SELECT id FROM users LIMIT 20;");
}
#[test]
fn mysql_pagination_wraps_wide_existing_limit_on_first_page() {
let result = build_paginated_query_sql(PaginatedQuerySqlOptions {
original_sql: "SELECT * FROM dy_promotion_item WHERE create_time < '2026-06-01' LIMIT 10000;".to_string(),
database_type: Some(DatabaseType::Mysql),
limit: 500,
offset: 0,
});
assert_eq!(
result.sql.unwrap(),
"SELECT * FROM (SELECT * FROM dy_promotion_item WHERE create_time < '2026-06-01' LIMIT 10000) `dbx_page` LIMIT 500 OFFSET 0;"
);
}
#[test]
fn mysql_pagination_wraps_comma_limit_row_count_on_first_page() {
let result = build_paginated_query_sql(PaginatedQuerySqlOptions {
original_sql: "SELECT * FROM users LIMIT 20, 10000;".to_string(),
database_type: Some(DatabaseType::Mysql),
limit: 500,
offset: 0,
});
assert_eq!(
result.sql.unwrap(),
"SELECT * FROM (SELECT * FROM users LIMIT 20, 10000) `dbx_page` LIMIT 500 OFFSET 0;"
);
}
#[test]
fn mysql_pagination_wraps_existing_limit_for_later_pages() {
let result = build_paginated_query_sql(PaginatedQuerySqlOptions {