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.JDBCException;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.dialect.function.SQLFunctionTemplate;
|
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.ConstraintViolationException;
|
||||||
import org.hibernate.exception.LockTimeoutException;
|
import org.hibernate.exception.LockTimeoutException;
|
||||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
|
@ -27,6 +29,8 @@ import org.hibernate.type.StandardBasicTypes;
|
||||||
*/
|
*/
|
||||||
public class SybaseASE157Dialect extends SybaseASE15Dialect {
|
public class SybaseASE157Dialect extends SybaseASE15Dialect {
|
||||||
|
|
||||||
|
private static final SybaseASE157LimitHandler LIMIT_HANDLER = new SybaseASE157LimitHandler();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a SybaseASE157Dialect
|
* 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