HHH-11503 - Fix SQL Server 2012 offset/fetch parameter binding.
This commit is contained in:
parent
2b79644b63
commit
652aa43427
|
@ -89,6 +89,6 @@ public class SQLServer2012Dialect extends SQLServer2008Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LimitHandler getDefaultLimitHandler() {
|
protected LimitHandler getDefaultLimitHandler() {
|
||||||
return SQLServer2012LimitHandler.INSTANCE;
|
return new SQLServer2012LimitHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect.pagination;
|
package org.hibernate.dialect.pagination;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
import org.hibernate.engine.spi.RowSelection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,9 +17,10 @@ import org.hibernate.engine.spi.RowSelection;
|
||||||
* @author Chris Cranford
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class SQLServer2012LimitHandler extends SQLServer2005LimitHandler {
|
public class SQLServer2012LimitHandler extends SQLServer2005LimitHandler {
|
||||||
public static final SQLServer2012LimitHandler INSTANCE = new SQLServer2012LimitHandler();
|
// determines whether the limit handler used offset/fetch or 2005 behavior.
|
||||||
|
private boolean usedOffsetFetch;
|
||||||
|
|
||||||
private SQLServer2012LimitHandler() {
|
public SQLServer2012LimitHandler() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +31,7 @@ public class SQLServer2012LimitHandler extends SQLServer2005LimitHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsVariableLimit() {
|
public boolean supportsVariableLimit() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,12 +44,70 @@ public class SQLServer2012LimitHandler extends SQLServer2005LimitHandler {
|
||||||
if ( !LimitHelper.useLimit( this, selection ) ) {
|
if ( !LimitHelper.useLimit( this, selection ) ) {
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
int firstRow = LimitHelper.hasFirstRow( selection ) ? selection.getFirstRow() : 0;
|
return applyOffsetFetch( selection, sql, getInsertPosition( sql ) );
|
||||||
return sql + String.format( " offset %d rows fetch next %d rows only", firstRow, selection.getMaxRows() );
|
|
||||||
}
|
}
|
||||||
return super.processSql( sql, selection );
|
return super.processSql( sql, selection );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useMaxForLimit() {
|
||||||
|
// when using the offset fetch clause, the max value is passed as-is.
|
||||||
|
// SQLServer2005LimitHandler uses start + max values.
|
||||||
|
return usedOffsetFetch ? false : super.useMaxForLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int convertToFirstRowValue(int zeroBasedFirstResult) {
|
||||||
|
// When using the offset/fetch clause, the first row is passed as-is
|
||||||
|
// SQLServer2005LimitHandler uses zeroBasedFirstResult + 1
|
||||||
|
if ( usedOffsetFetch ) {
|
||||||
|
return zeroBasedFirstResult;
|
||||||
|
}
|
||||||
|
return super.convertToFirstRowValue( zeroBasedFirstResult );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int bindLimitParametersAtEndOfQuery(RowSelection selection, PreparedStatement statement, int index)
|
||||||
|
throws SQLException {
|
||||||
|
if ( usedOffsetFetch && !LimitHelper.hasFirstRow( selection ) ) {
|
||||||
|
// apply just the max value when offset fetch applied
|
||||||
|
statement.setInt( index, getMaxOrLimit( selection ) );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return super.bindLimitParametersAtEndOfQuery( selection, statement, index );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOffsetFetch(RowSelection selection) {
|
||||||
|
if ( !LimitHelper.hasFirstRow( selection ) ) {
|
||||||
|
return " offset 0 rows fetch next ? rows only";
|
||||||
|
}
|
||||||
|
return " offset ? rows fetch next ? rows only";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInsertPosition(String sql) {
|
||||||
|
int position = sql.length() - 1;
|
||||||
|
for ( ; position > 0; --position ) {
|
||||||
|
char ch = sql.charAt( position );
|
||||||
|
if ( ch != ';' && ch != ' ' && ch != '\r' && ch != '\n' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return position + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String applyOffsetFetch(RowSelection selection, String sql, int position) {
|
||||||
|
usedOffsetFetch = true;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append( sql.substring( 0, position ) );
|
||||||
|
sb.append( getOffsetFetch( selection ) );
|
||||||
|
if ( position > sql.length() ) {
|
||||||
|
sb.append( sql.substring( position - 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasOrderBy(String sql) {
|
private boolean hasOrderBy(String sql) {
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
for ( int i = 0; i < sql.length(); ++i ) {
|
for ( int i = 0; i < sql.length(); ++i ) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class SQLServer2012DialectTestCase extends BaseUnitTestCase {
|
||||||
public void testGetLimitStringMaxRowsOnly() {
|
public void testGetLimitStringMaxRowsOnly() {
|
||||||
final String input = "select distinct f1 as f53245 from table846752 order by f234, f67 desc";
|
final String input = "select distinct f1 as f53245 from table846752 order by f234, f67 desc";
|
||||||
assertEquals(
|
assertEquals(
|
||||||
input + " offset 0 rows fetch next 10 rows only",
|
input + " offset 0 rows fetch next ? rows only",
|
||||||
dialect.getLimitHandler().processSql( input, toRowSelection( 0, 10 ) ).toLowerCase( Locale.ROOT )
|
dialect.getLimitHandler().processSql( input, toRowSelection( 0, 10 ) ).toLowerCase( Locale.ROOT )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class SQLServer2012DialectTestCase extends BaseUnitTestCase {
|
||||||
public void testGetLimitStringWithOffsetAndMaxRows() {
|
public void testGetLimitStringWithOffsetAndMaxRows() {
|
||||||
final String input = "select distinct f1 as f53245 from table846752 order by f234, f67 desc";
|
final String input = "select distinct f1 as f53245 from table846752 order by f234, f67 desc";
|
||||||
assertEquals(
|
assertEquals(
|
||||||
input + " offset 5 rows fetch next 25 rows only",
|
input + " offset ? rows fetch next ? rows only",
|
||||||
dialect.getLimitHandler().processSql( input, toRowSelection( 5, 25 ) ).toLowerCase( Locale.ROOT )
|
dialect.getLimitHandler().processSql( input, toRowSelection( 5, 25 ) ).toLowerCase( Locale.ROOT )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue