fix(core): use row_number pagination for sqlserver table transfers
Closes #7356
This commit is contained in:
1 file changed
+97
-10
@@ -1895,6 +1895,22 @@ fn oracle_rownum_page_sql(col_list: &str, base_sql: String, offset: u64, limit:
|
||||
)
|
||||
}
|
||||
|
||||
// SQL Server 2008 R2 and older reject `OFFSET ... FETCH` (added in SQL Server
|
||||
// 2012), so paged reads must use a ROW_NUMBER() subquery that every supported
|
||||
// SQL Server version accepts (issue #7356).
|
||||
fn sqlserver_row_number_page_sql(
|
||||
col_list: &str,
|
||||
from_clause: &str,
|
||||
order_by: &str,
|
||||
offset: u64,
|
||||
limit: usize,
|
||||
) -> String {
|
||||
let end = offset + limit as u64;
|
||||
format!(
|
||||
"SELECT {col_list} FROM (SELECT {col_list}, ROW_NUMBER() OVER (ORDER BY {order_by}) AS __dbx_row_num FROM {from_clause}) AS __dbx_page WHERE __dbx_row_num > {offset} AND __dbx_row_num <= {end}"
|
||||
)
|
||||
}
|
||||
|
||||
fn postgres_index_column_sql(column: &str) -> String {
|
||||
if is_simple_identifier(column) {
|
||||
quote_identifier(column, &DatabaseType::Postgres)
|
||||
@@ -4055,7 +4071,10 @@ pub fn pagination_sql(
|
||||
format!("SELECT SKIP {offset} FIRST {limit} {col_list} FROM {full_table}")
|
||||
}
|
||||
}
|
||||
DatabaseType::SqlServer | DatabaseType::Dameng => {
|
||||
DatabaseType::SqlServer => {
|
||||
sqlserver_row_number_page_sql(&col_list, &full_table, "(SELECT NULL)", offset, limit)
|
||||
}
|
||||
DatabaseType::Dameng => {
|
||||
format!(
|
||||
"SELECT {col_list} FROM {full_table} ORDER BY (SELECT NULL) OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY"
|
||||
)
|
||||
@@ -4098,7 +4117,11 @@ pub fn pagination_sql_with_order(
|
||||
format!("SELECT SKIP {offset} FIRST {limit} {col_list} FROM {full_table}{order_by}")
|
||||
}
|
||||
}
|
||||
DatabaseType::SqlServer | DatabaseType::Dameng => {
|
||||
DatabaseType::SqlServer => {
|
||||
let order_by = order_expression.unwrap_or_else(|| "(SELECT NULL)".to_string());
|
||||
sqlserver_row_number_page_sql(&col_list, &full_table, &order_by, offset, limit)
|
||||
}
|
||||
DatabaseType::Dameng => {
|
||||
let order_by = order_expression.unwrap_or_else(|| "(SELECT NULL)".to_string());
|
||||
format!(
|
||||
"SELECT {col_list} FROM {full_table} ORDER BY {order_by} OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY"
|
||||
@@ -4182,7 +4205,12 @@ pub fn pagination_sql_with_filter_order_and_identifier_quote(
|
||||
format!("SELECT SKIP {offset} FIRST {limit} {col_list} FROM {full_table}{where_clause}{order_by}")
|
||||
}
|
||||
}
|
||||
DatabaseType::SqlServer | DatabaseType::Dameng => {
|
||||
DatabaseType::SqlServer => {
|
||||
let order_by = order_expression.unwrap_or_else(|| "(SELECT NULL)".to_string());
|
||||
let from_clause = format!("{full_table}{where_clause}");
|
||||
sqlserver_row_number_page_sql(&col_list, &from_clause, &order_by, offset, limit)
|
||||
}
|
||||
DatabaseType::Dameng => {
|
||||
let order_by = order_expression.unwrap_or_else(|| "(SELECT NULL)".to_string());
|
||||
format!(
|
||||
"SELECT {col_list} FROM {full_table}{where_clause} ORDER BY {order_by} OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY"
|
||||
@@ -4282,7 +4310,10 @@ pub fn keyset_pagination_sql_with_identifier_quote(
|
||||
DatabaseType::Informix => {
|
||||
format!("SELECT FIRST {limit} {col_list} FROM {full_table}{where_clause} ORDER BY {order}")
|
||||
}
|
||||
DatabaseType::SqlServer | DatabaseType::Dameng => {
|
||||
DatabaseType::SqlServer => {
|
||||
format!("SELECT TOP ({limit}) {col_list} FROM {full_table}{where_clause} ORDER BY {order}")
|
||||
}
|
||||
DatabaseType::Dameng => {
|
||||
format!(
|
||||
"SELECT {col_list} FROM {full_table}{where_clause} ORDER BY {order} OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY"
|
||||
)
|
||||
@@ -10500,6 +10531,65 @@ mod tests {
|
||||
assert!(!sql.contains(" LIMIT "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_export_pagination_uses_row_number_subquery() {
|
||||
let sql = pagination_sql(
|
||||
&[String::from("id"), String::from("name")],
|
||||
"users",
|
||||
"dbo",
|
||||
&DatabaseType::SqlServer,
|
||||
500,
|
||||
100,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT [id], [name] FROM (SELECT [id], [name], ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS __dbx_row_num FROM [dbo].[users]) AS __dbx_page WHERE __dbx_row_num > 500 AND __dbx_row_num <= 600"
|
||||
);
|
||||
assert!(!sql.contains("OFFSET"));
|
||||
assert!(!sql.contains(" FETCH "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_ordered_pagination_uses_row_number_subquery() {
|
||||
let sql = pagination_sql_with_order(
|
||||
&[String::from("id"), String::from("name")],
|
||||
"users",
|
||||
"dbo",
|
||||
&DatabaseType::SqlServer,
|
||||
200,
|
||||
100,
|
||||
&[String::from("id")],
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(sql.contains("ROW_NUMBER() OVER (ORDER BY [id]) AS __dbx_row_num"));
|
||||
assert!(sql.contains("WHERE __dbx_row_num > 200 AND __dbx_row_num <= 300"));
|
||||
assert!(!sql.contains("OFFSET"));
|
||||
assert!(!sql.contains(" FETCH "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_filtered_pagination_preserves_filter_in_subquery() {
|
||||
let sql = pagination_sql_with_filter_order(
|
||||
&[String::from("id"), String::from("status")],
|
||||
"users",
|
||||
"dbo",
|
||||
&DatabaseType::SqlServer,
|
||||
10_000,
|
||||
2_000,
|
||||
Some("WHERE status = 'active'"),
|
||||
Some("[id] DESC"),
|
||||
&[String::from("id")],
|
||||
);
|
||||
|
||||
assert!(sql.contains("ROW_NUMBER() OVER (ORDER BY [id] DESC) AS __dbx_row_num"));
|
||||
assert!(sql.contains("FROM [dbo].[users] WHERE (status = 'active')"));
|
||||
assert!(sql.contains("WHERE __dbx_row_num > 10000 AND __dbx_row_num <= 12000"));
|
||||
assert!(!sql.contains("OFFSET"));
|
||||
assert!(!sql.contains(" FETCH "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filtered_pagination_preserves_where_and_order() {
|
||||
let sql = pagination_sql_with_filter_order(
|
||||
@@ -10572,7 +10662,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlserver_keyset_pagination_includes_offset_fetch() {
|
||||
fn sqlserver_keyset_pagination_uses_top() {
|
||||
let sql = keyset_pagination_sql(
|
||||
&[String::from("id"), String::from("name")],
|
||||
"users",
|
||||
@@ -10583,10 +10673,7 @@ mod tests {
|
||||
100,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT [id], [name] FROM [dbo].[users] ORDER BY [id] ASC OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY"
|
||||
);
|
||||
assert_eq!(sql, "SELECT TOP (100) [id], [name] FROM [dbo].[users] ORDER BY [id] ASC");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -10642,7 +10729,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT [tenant_id], [id], [name] FROM [dbo].[users] WHERE ([tenant_id] > 10 OR ([tenant_id] = 10 AND [id] > 25)) ORDER BY [tenant_id] ASC, [id] ASC OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY"
|
||||
"SELECT TOP (100) [tenant_id], [id], [name] FROM [dbo].[users] WHERE ([tenant_id] > 10 OR ([tenant_id] = 10 AND [id] > 25)) ORDER BY [tenant_id] ASC, [id] ASC"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user