diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java index dfc1fa10a97..3dea68e20ac 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java @@ -45,8 +45,8 @@ * @since TOPLink/Java 1.0 */ public class SQLServerPlatform extends org.eclipse.persistence.platform.database.DatabasePlatform { - /** Support for sequence objects added in SQL Server 2012 */ - private boolean supportsSequenceObjects; + /** Support for sequence objects and OFFSET FETCH NEXT added in SQL Server 2012 */ + private boolean isVersion11OrHigher; private boolean isConnectionDataInitialized; public SQLServerPlatform(){ @@ -62,7 +62,7 @@ public void initializeConnectionData(Connection connection) throws SQLException } DatabaseMetaData dmd = connection.getMetaData(); int databaseVersion = dmd.getDatabaseMajorVersion(); - supportsSequenceObjects = databaseVersion >= 11; + isVersion11OrHigher = databaseVersion >= 11; isConnectionDataInitialized = true; this.driverSupportsNationalCharacterVarying = Helper.compareVersions(dmd.getDriverVersion(), "4.0.0") >= 0; } @@ -690,7 +690,7 @@ public boolean supportsIdentity() { */ @Override public boolean supportsSequenceObjects() { - return supportsSequenceObjects; + return isVersion11OrHigher; } /** @@ -732,4 +732,47 @@ public void writeUpdateOriginalFromTempTableSql(Writer writer, DatabaseTable tab writer.write(tempTableName); writeAutoJoinWhereClause(writer, tableName, tempTableName, pkFields, this); } + + @Override + public void printSQLSelectStatement(DatabaseCall call, ExpressionSQLPrinter printer, SQLSelectStatement statement) { + ReadQuery query = statement.getQuery(); + if (query == null || !isVersion11OrHigher || !shouldUseRownumFiltering()) { + super.printSQLSelectStatement(call, printer, statement); + return; + } + + int max = Math.max(0, query.getMaxRows()); + int first = Math.max(0, query.getFirstResult()); + + if (max == 0 && first == 0) { + super.printSQLSelectStatement(call, printer, statement); + return; + } + + // OFFSET + FETCH NEXT requires ORDER BY, so add an ordering if there are none + // this SQL will satisfy the query parser without actually changing the ordering of the rows + List orderBy = statement.getOrderByExpressions(); + if (orderBy.isEmpty()) { + orderBy.add(statement.getBuilder().literal("ROW_NUMBER() OVER (ORDER BY (SELECT null))")); + } + + // decide exact syntax to use, depending on whether a limit is specified (could just have an offset) + String offsetFetchSql; + List offsetFetchArgs; + if (max == 0) { + offsetFetchSql = "? OFFSET ? ROWS"; + offsetFetchArgs = Arrays.asList(first); + } else { + offsetFetchSql = "? OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + offsetFetchArgs = Arrays.asList(first, max - first); + } + + // append to the last ORDER BY clause + orderBy.add(orderBy.remove(orderBy.size() - 1).sql(offsetFetchSql, offsetFetchArgs)); + + super.printSQLSelectStatement(call, printer, statement); + + call.setIgnoreFirstRowSetting(true); + call.setIgnoreMaxResultsSetting(true); + } }