From efe8a2e4fb97e24047b09247218b71a5a46995ef Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 26 May 2026 14:44:10 +0800 Subject: [PATCH] fix: avoid mysql pagination wrapper for duplicate columns --- crates/dbx-core/src/query_result_sql.rs | 45 ++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/crates/dbx-core/src/query_result_sql.rs b/crates/dbx-core/src/query_result_sql.rs index 09515b6a4..310edf425 100644 --- a/crates/dbx-core/src/query_result_sql.rs +++ b/crates/dbx-core/src/query_result_sql.rs @@ -164,6 +164,10 @@ pub fn build_paginated_query_sql(options: PaginatedQuerySqlOptions) -> QuerySqlB return ok(add_sql_server_top(&statement, safe_limit)); } + if options.database_type == Some(DatabaseType::Mysql) { + return ok(add_mysql_limit(&statement, safe_limit, safe_offset)); + } + let alias = quote_table_identifier(options.database_type, "dbx_page"); let base = format!("SELECT * FROM ({statement}) {alias}"); @@ -341,6 +345,18 @@ fn sql_server_statement_for_derived_table(statement: &str) -> String { statement[..order_by].trim_end().to_string() } +fn add_mysql_limit(statement: &str, limit: usize, offset: usize) -> String { + if has_top_level_limit(statement) { + return format!("{statement};"); + } + let offset_sql = if offset > 0 { format!(" OFFSET {offset}") } else { String::new() }; + format!("{statement} LIMIT {limit}{offset_sql};") +} + +fn has_top_level_limit(sql: &str) -> bool { + top_level_sql_tokens(sql).iter().any(|token| token.text == "LIMIT") +} + fn find_top_level_trailing_order_by(sql: &str) -> Option { let tokens = top_level_sql_tokens(sql); for index in (0..tokens.len().saturating_sub(1)).rev() { @@ -650,7 +666,34 @@ mod tests { offset: 0, }); - assert_eq!(result.sql.unwrap(), "SELECT * FROM (SELECT id FROM users WHERE active = 1) `dbx_page` LIMIT 50;"); + assert_eq!(result.sql.unwrap(), "SELECT id FROM users WHERE active = 1 LIMIT 50;"); + } + + #[test] + fn mysql_pagination_does_not_wrap_duplicate_result_columns() { + let result = build_paginated_query_sql(PaginatedQuerySqlOptions { + original_sql: "SELECT p.id, t.id FROM table1 p LEFT JOIN table2 t ON p.f = t.f".to_string(), + database_type: Some(DatabaseType::Mysql), + limit: 50, + offset: 100, + }); + + assert_eq!( + result.sql.unwrap(), + "SELECT p.id, t.id FROM table1 p LEFT JOIN table2 t ON p.f = t.f LIMIT 50 OFFSET 100;" + ); + } + + #[test] + fn mysql_pagination_keeps_existing_top_level_limit() { + let result = build_paginated_query_sql(PaginatedQuerySqlOptions { + original_sql: "SELECT id FROM users LIMIT 20;".to_string(), + database_type: Some(DatabaseType::Mysql), + limit: 50, + offset: 0, + }); + + assert_eq!(result.sql.unwrap(), "SELECT id FROM users LIMIT 20;"); } #[test]