HHH-12196 Implement a naive limit handler for Sybase
It doesn't manage all the corner cases but it should be safe enough as only triggered in the simple cases.
This commit is contained in:
parent
5d965f8e15
commit
b66df9a352
|
@ -12,6 +12,8 @@ import java.util.Map;
|
|||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.function.SQLFunctionTemplate;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.SybaseASE157LimitHandler;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
|
@ -27,6 +29,8 @@ import org.hibernate.type.StandardBasicTypes;
|
|||
*/
|
||||
public class SybaseASE157Dialect extends SybaseASE15Dialect {
|
||||
|
||||
private static final SybaseASE157LimitHandler LIMIT_HANDLER = new SybaseASE157LimitHandler();
|
||||
|
||||
/**
|
||||
* Constructs a SybaseASE157Dialect
|
||||
*/
|
||||
|
@ -102,4 +106,19 @@ public class SybaseASE157Dialect extends SybaseASE15Dialect {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimitOffset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return LIMIT_HANDLER;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.dialect.pagination;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
|
||||
/**
|
||||
* This limit handler is very conservative and is only triggered in simple cases involving a select or select distinct.
|
||||
* <p>
|
||||
* Note that if the query already contains "top" just after the select or select distinct, we don't add anything to the
|
||||
* query. It might just be a column name but, in any case, we just don't add the top clause and default to the previous
|
||||
* behavior so it's not an issue.
|
||||
*/
|
||||
public class SybaseASE157LimitHandler extends AbstractLimitHandler {
|
||||
|
||||
private static final Pattern SELECT_DISTINCT_PATTERN = Pattern.compile( "^(\\s*select\\s+distinct\\s+).*",
|
||||
Pattern.CASE_INSENSITIVE );
|
||||
private static final Pattern SELECT_PATTERN = Pattern.compile( "^(\\s*select\\s+).*", Pattern.CASE_INSENSITIVE );
|
||||
private static final Pattern TOP_PATTERN = Pattern.compile( "^\\s*top\\s+.*", Pattern.CASE_INSENSITIVE );
|
||||
|
||||
@Override
|
||||
public String processSql(String sql, RowSelection selection) {
|
||||
if ( selection.getMaxRows() == null ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
int top = getMaxOrLimit( selection );
|
||||
if ( top == Integer.MAX_VALUE ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
Matcher selectDistinctMatcher = SELECT_DISTINCT_PATTERN.matcher( sql );
|
||||
if ( selectDistinctMatcher.matches() ) {
|
||||
return insertTop( selectDistinctMatcher, sql, top );
|
||||
}
|
||||
|
||||
Matcher selectMatcher = SELECT_PATTERN.matcher( sql );
|
||||
if ( selectMatcher.matches() ) {
|
||||
return insertTop( selectMatcher, sql, top );
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimitOffset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMaxForLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsVariableLimit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String insertTop(Matcher matcher, String sql, int top) {
|
||||
int end = matcher.end( 1 );
|
||||
|
||||
if ( TOP_PATTERN.matcher( sql.substring( end ) ).matches() ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder( sql );
|
||||
sb.insert( end, "top " + top + " " );
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.hibernate.dialect.pagination.SybaseASE157LimitHandler;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SybaseASE157LimitHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testLimitHandler() {
|
||||
assertEquals( "select * from entity", processSql( "select * from entity", null, null ) );
|
||||
assertEquals( "select * from entity", processSql( "select * from entity", 15, null ) );
|
||||
assertEquals( "select top 15 * from entity", processSql( "select * from entity", null, 15 ) );
|
||||
assertEquals( "select top 18 * from entity", processSql( "select * from entity", 3, 15 ) );
|
||||
assertEquals( "SELECT top 18 * FROM entity", processSql( "SELECT * FROM entity", 3, 15 ) );
|
||||
assertEquals( " select top 18 * from entity", processSql( " select * from entity", 3, 15 ) );
|
||||
assertEquals( "selectand", processSql( "selectand", 3, 15 ) );
|
||||
assertEquals( "select distinct top 15 id from entity",
|
||||
processSql( "select distinct id from entity", null, 15 ) );
|
||||
assertEquals( "select distinct top 18 id from entity", processSql( "select distinct id from entity", 3, 15 ) );
|
||||
assertEquals( " select distinct top 18 id from entity",
|
||||
processSql( " select distinct id from entity", 3, 15 ) );
|
||||
assertEquals(
|
||||
"WITH employee AS (SELECT * FROM Employees) SELECT * FROM employee WHERE ID < 20 UNION ALL SELECT * FROM employee WHERE Sex = 'M'",
|
||||
processSql(
|
||||
"WITH employee AS (SELECT * FROM Employees) SELECT * FROM employee WHERE ID < 20 UNION ALL SELECT * FROM employee WHERE Sex = 'M'",
|
||||
3, 15 ) );
|
||||
|
||||
assertEquals( "select top 5 * from entity", processSql( "select top 5 * from entity", 3, 15 ) );
|
||||
assertEquals( "select distinct top 7 * from entity", processSql( "select distinct top 7 * from entity", 3, 15 ) );
|
||||
assertEquals( "select distinct top 18 top_column from entity", processSql( "select distinct top_column from entity", 3, 15 ) );
|
||||
}
|
||||
|
||||
private String processSql(String sql, Integer offset, Integer limit) {
|
||||
RowSelection rowSelection = new RowSelection();
|
||||
if ( offset != null ) {
|
||||
rowSelection.setFirstRow( offset );
|
||||
}
|
||||
if (limit != null) {
|
||||
rowSelection.setMaxRows( limit );
|
||||
}
|
||||
|
||||
SybaseASE157LimitHandler limitHandler = new SybaseASE157LimitHandler();
|
||||
return limitHandler.processSql( sql, rowSelection );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue