From af91751a15ca2d5143d06603fffd323bc796dc4a Mon Sep 17 00:00:00 2001 From: HandSonic <8078023+handsonic@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:11:44 +0800 Subject: [PATCH] fix(sqlserver): add TOP/ROW_NUMBER pagination for SQL Server < 2012 (#1693) buildPageLimit() returned empty string for SQL Server versions below 11 (< 2012), causing DefaultSQLExecutor to skip pagination entirely and return all rows. Add fallback pagination: - SQL Server 2012+ (v11+): OFFSET/FETCH NEXT (existing behavior) - SQL Server 2005-2008 R2 (v9-10): TOP for first page, ROW_NUMBER() OVER() for subsequent pages - Below v9: returns empty (no safe pagination method available) --- .../builder/SqlServerSqlBuilder.java | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/builder/SqlServerSqlBuilder.java b/chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/builder/SqlServerSqlBuilder.java index 6239e7beb3..730cae5018 100644 --- a/chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/builder/SqlServerSqlBuilder.java +++ b/chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/builder/SqlServerSqlBuilder.java @@ -243,19 +243,34 @@ public String buildPageLimit(PageLimitRequest request) { String version = Chat2DBContext.getDbVersion(); if (StringUtils.isNotBlank(version)) { String[] versions = version.split(VALUE_BACKSLASH_DOT); - if (versions.length > 0 && Integer.parseInt(versions[0]) >= 11) { - StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14); - sqlBuilder.append(sql); - if (!sql.toLowerCase().contains(ORDER_BY_KEYWORD_LOWER)) { - sqlBuilder.append(SQL_ORDER_BY_OPEN_PAREN_SELECT_NULL_CLOSE_PAREN); + if (versions.length > 0) { + int majorVersion = Integer.parseInt(versions[0]); + if (majorVersion >= 11) { + // SQL Server 2012+: use OFFSET/FETCH NEXT + StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14); + sqlBuilder.append(sql); + if (!sql.toLowerCase().contains(ORDER_BY_KEYWORD_LOWER)) { + sqlBuilder.append(SQL_ORDER_BY_OPEN_PAREN_SELECT_NULL_CLOSE_PAREN); + } + sqlBuilder.append(SQLConstants.LINE_SEPARATOR_OFFSET_SQL); + sqlBuilder.append(offset); + sqlBuilder.append(SQLConstants.ROWS_SQL); + sqlBuilder.append(SQLConstants.FETCH_NEXT_SQL); + sqlBuilder.append(pageSize); + sqlBuilder.append(SQLConstants.ROWS_ONLY_SQL); + return sqlBuilder.toString(); + } else if (majorVersion >= 9) { + // SQL Server 2005-2008 R2: use ROW_NUMBER() OVER() + if (offset == 0) { + // First page: use TOP directly + return "SELECT TOP (" + pageSize + ") * FROM (" + sql + ") AS _page_query"; + } else { + // Subsequent pages: use ROW_NUMBER() for offset + return "SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS _row_num " + + "FROM (" + sql + ") AS _inner_query) AS _outer_query " + + "WHERE _row_num > " + offset + " AND _row_num <= " + (offset + pageSize); + } } - sqlBuilder.append(SQLConstants.LINE_SEPARATOR_OFFSET_SQL); - sqlBuilder.append(offset); - sqlBuilder.append(SQLConstants.ROWS_SQL); - sqlBuilder.append(SQLConstants.FETCH_NEXT_SQL); - sqlBuilder.append(pageSize); - sqlBuilder.append(SQLConstants.ROWS_ONLY_SQL); - return sqlBuilder.toString(); } } return SQLConstants.EMPTY;