fix(sqlserver): paginate joins with duplicate result columns
Closes #5284
This commit is contained in:
3 files changed
+217
-31
No files matched your search
@@ -1,4 +1,6 @@
|
||||
use dbx_core::db::sqlserver;
|
||||
use dbx_core::models::connection::DatabaseType;
|
||||
use dbx_core::query_result_sql::{build_paginated_query_sql, PaginatedQuerySqlOptions};
|
||||
use std::time::Duration;
|
||||
|
||||
async fn connect_sqlserver() -> sqlserver::SqlServerClient {
|
||||
@@ -71,3 +73,79 @@ async fn sqlserver_single_result_query_drains_later_results() {
|
||||
.expect("execute query after draining later results");
|
||||
assert_eq!(follow_up.rows[0][0].as_i64(), Some(2));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires DBX_TEST_SQLSERVER_HOST and DBX_TEST_SQLSERVER_PASSWORD"]
|
||||
async fn sqlserver_duplicate_join_columns_page_without_leaking_rowcount() {
|
||||
let mut client = connect_sqlserver().await;
|
||||
sqlserver::execute_simple_batch_with_max_rows(
|
||||
&mut client,
|
||||
"CREATE TABLE #dbx_page_detail (id INT NOT NULL PRIMARY KEY, parent_id INT NOT NULL); \
|
||||
CREATE TABLE #dbx_page_main (id INT NOT NULL PRIMARY KEY, label NVARCHAR(20) NOT NULL); \
|
||||
WITH numbers AS ( \
|
||||
SELECT TOP (1200) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS value \
|
||||
FROM sys.all_objects a CROSS JOIN sys.all_objects b \
|
||||
) \
|
||||
INSERT INTO #dbx_page_main (id, label) SELECT value, CONCAT(N'label-', value) FROM numbers; \
|
||||
INSERT INTO #dbx_page_detail (id, parent_id) SELECT id, id FROM #dbx_page_main;",
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("create pagination fixtures");
|
||||
|
||||
let reported_query = "SELECT * FROM #dbx_page_detail d LEFT JOIN #dbx_page_main m ON m.id = d.parent_id";
|
||||
let reported_page = build_paginated_query_sql(PaginatedQuerySqlOptions {
|
||||
original_sql: reported_query.to_string(),
|
||||
database_type: Some(DatabaseType::SqlServer),
|
||||
limit: 500,
|
||||
offset: 500,
|
||||
});
|
||||
let reported_results = sqlserver::execute_batch_with_max_rows(
|
||||
&mut client,
|
||||
reported_page.sql.as_deref().expect("build reported pagination SQL"),
|
||||
Some(500),
|
||||
)
|
||||
.await
|
||||
.expect("execute reported duplicate-column page");
|
||||
assert_eq!(reported_results.first().expect("reported page result").rows.len(), 500);
|
||||
|
||||
let query = format!("{reported_query} ORDER BY d.id");
|
||||
let page = build_paginated_query_sql(PaginatedQuerySqlOptions {
|
||||
original_sql: query,
|
||||
database_type: Some(DatabaseType::SqlServer),
|
||||
limit: 500,
|
||||
offset: 500,
|
||||
});
|
||||
let page_sql = page.sql.expect("build duplicate-column pagination SQL");
|
||||
let results = sqlserver::execute_batch_with_max_rows(&mut client, &page_sql, Some(500))
|
||||
.await
|
||||
.expect("execute duplicate-column page");
|
||||
let result = results.first().expect("page result");
|
||||
|
||||
assert_eq!(result.rows.len(), 500);
|
||||
assert_eq!(result.rows.first().and_then(|row| row[0].as_i64()), Some(501));
|
||||
assert_eq!(result.rows.last().and_then(|row| row[0].as_i64()), Some(1000));
|
||||
|
||||
let failing_page = build_paginated_query_sql(PaginatedQuerySqlOptions {
|
||||
original_sql:
|
||||
"SELECT * FROM #dbx_page_detail d LEFT JOIN #dbx_missing_page_table m ON m.id = d.parent_id ORDER BY d.id"
|
||||
.to_string(),
|
||||
database_type: Some(DatabaseType::SqlServer),
|
||||
limit: 1,
|
||||
offset: 1,
|
||||
});
|
||||
let error = sqlserver::execute_batch_with_max_rows(
|
||||
&mut client,
|
||||
failing_page.sql.as_deref().expect("build failing pagination SQL"),
|
||||
Some(1),
|
||||
)
|
||||
.await
|
||||
.expect_err("missing joined table should fail");
|
||||
assert!(error.to_ascii_lowercase().contains("dbx_missing_page_table"));
|
||||
|
||||
let follow_up =
|
||||
sqlserver::execute_query(&mut client, "SELECT value FROM (VALUES (1), (2)) rows(value) ORDER BY value")
|
||||
.await
|
||||
.expect("execute query after ROWCOUNT page");
|
||||
assert_eq!(follow_up.rows.len(), 2);
|
||||
}
|
||||
Reference in new issue
Block a user