HHH-17409 Support offset without limit in some LimitHandlers
This commit is contained in:
parent
4087774691
commit
016dc56208
|
@ -146,7 +146,7 @@ public class H2LegacyDialect extends Dialect {
|
|||
// https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440
|
||||
limitHandler = version.isSameOrAfter( 1, 4, 195 )
|
||||
? OffsetFetchLimitHandler.INSTANCE
|
||||
: LimitOffsetLimitHandler.INSTANCE;
|
||||
: LimitOffsetLimitHandler.OFFSET_ONLY_INSTANCE;
|
||||
|
||||
if ( version.isBefore( 1, 2, 139 ) ) {
|
||||
LOG.unsupportedMultiTableBulkHqlJpaql( version.getMajor(), version.getMinor(), version.getMicro() );
|
||||
|
|
|
@ -422,7 +422,7 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return getVersion().isBefore( 2 ) ? LegacyHSQLLimitHandler.INSTANCE
|
||||
: getVersion().isBefore( 2, 5 ) ? LimitOffsetLimitHandler.INSTANCE
|
||||
: getVersion().isBefore( 2, 5 ) ? LimitOffsetLimitHandler.OFFSET_ONLY_INSTANCE
|
||||
: OffsetFetchLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.dialect.AbstractTransactSQLDialect;
|
|||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitOffsetLimitHandler;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.temptable.TemporaryTable;
|
||||
|
@ -130,7 +131,7 @@ public class MaxDBDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return LimitOffsetLimitHandler.INSTANCE;
|
||||
return LimitLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -750,7 +750,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return getVersion().isBefore( 8, 4 )
|
||||
? LimitOffsetLimitHandler.INSTANCE
|
||||
? LimitOffsetLimitHandler.OFFSET_ONLY_INSTANCE
|
||||
: OffsetFetchLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ public class RowsLimitHandler extends AbstractSimpleLimitHandler {
|
|||
return hasFirstRow ? " rows ? to ?" : " rows ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String offsetOnlyClause() {
|
||||
return " rows ? to " + Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean useMaxForLimit() {
|
||||
return true;
|
||||
|
@ -48,4 +53,9 @@ public class RowsLimitHandler extends AbstractSimpleLimitHandler {
|
|||
protected Pattern getForUpdatePattern() {
|
||||
return FOR_UPDATE_PATTERN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOffset() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,6 @@ public class OracleDialect extends Dialect {
|
|||
|
||||
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 19 );
|
||||
|
||||
private final LimitHandler limitHandler = Oracle12LimitHandler.INSTANCE;
|
||||
private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
|
||||
|
||||
// Is it an Autonomous Database Cloud Service?
|
||||
|
@ -939,7 +938,7 @@ public class OracleDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return limitHandler;
|
||||
return Oracle12LimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,9 +18,17 @@ public abstract class AbstractSimpleLimitHandler extends AbstractLimitHandler {
|
|||
|
||||
protected abstract String limitClause(boolean hasFirstRow);
|
||||
|
||||
protected String offsetOnlyClause() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processSql(String sql, Limit limit) {
|
||||
if ( !hasMaxRows( limit ) ) {
|
||||
final String offsetOnlyClause = offsetOnlyClause();
|
||||
if ( offsetOnlyClause != null && hasFirstRow( limit ) ) {
|
||||
return insert( offsetOnlyClause, sql );
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
return insert( limitClause( hasFirstRow( limit ) ), sql );
|
||||
|
@ -39,4 +47,9 @@ public abstract class AbstractSimpleLimitHandler extends AbstractLimitHandler {
|
|||
public final boolean supportsVariableLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOffset() {
|
||||
return super.supportsOffset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,11 @@ public class LimitLimitHandler extends AbstractSimpleLimitHandler {
|
|||
return hasFirstRow ? " limit ?,?" : " limit ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String offsetOnlyClause() {
|
||||
return " limit ?," + Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
private static final Pattern FOR_UPDATE_PATTERN =
|
||||
compile("\\s+for\\s+update\\b|\\s+lock\\s+in\\s+shared\\s+mode\\b|\\s*(;|$)", CASE_INSENSITIVE);
|
||||
|
||||
|
@ -35,4 +40,9 @@ public class LimitLimitHandler extends AbstractSimpleLimitHandler {
|
|||
protected Pattern getForUpdatePattern() {
|
||||
return FOR_UPDATE_PATTERN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOffset() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,30 @@ package org.hibernate.dialect.pagination;
|
|||
public class LimitOffsetLimitHandler extends AbstractSimpleLimitHandler {
|
||||
|
||||
public static LimitOffsetLimitHandler INSTANCE = new LimitOffsetLimitHandler();
|
||||
public static LimitOffsetLimitHandler OFFSET_ONLY_INSTANCE = new LimitOffsetLimitHandler() {
|
||||
@Override
|
||||
protected String offsetOnlyClause() {
|
||||
return " offset ?";
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected String limitClause(boolean hasFirstRow) {
|
||||
return hasFirstRow ? " limit ? offset ?" : " limit ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String offsetOnlyClause() {
|
||||
return " limit " + Integer.MAX_VALUE + " offset ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean bindLimitParametersInReverseOrder() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOffset() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,18 +33,23 @@ public class Oracle12LimitHandler extends AbstractLimitHandler {
|
|||
|
||||
@Override
|
||||
public String processSql(String sql, Limit limit, QueryOptions queryOptions) {
|
||||
final boolean hasMaxRows = hasMaxRows( limit );
|
||||
if ( !hasMaxRows ) {
|
||||
return sql;
|
||||
}
|
||||
return processSql(
|
||||
sql,
|
||||
hasFirstRow( limit ),
|
||||
hasMaxRows( limit ),
|
||||
queryOptions.getLockOptions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #processSql(String, boolean, boolean, LockOptions)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected String processSql(String sql, boolean hasFirstRow, LockOptions lockOptions) {
|
||||
return processSql( sql, hasFirstRow, true, lockOptions );
|
||||
}
|
||||
|
||||
protected String processSql(String sql, boolean hasFirstRow, boolean hasMaxRows, LockOptions lockOptions) {
|
||||
if ( lockOptions != null ) {
|
||||
final LockMode lockMode = lockOptions.getLockMode();
|
||||
switch ( lockMode ) {
|
||||
|
@ -53,42 +58,59 @@ public class Oracle12LimitHandler extends AbstractLimitHandler {
|
|||
case UPGRADE_NOWAIT:
|
||||
case PESSIMISTIC_FORCE_INCREMENT:
|
||||
case UPGRADE_SKIPLOCKED: {
|
||||
return processSql( sql, getForUpdateIndex( sql ), hasFirstRow );
|
||||
return processSql( sql, getForUpdateIndex( sql ), hasFirstRow, hasMaxRows );
|
||||
}
|
||||
default: {
|
||||
return processSqlOffsetFetch( sql, hasFirstRow );
|
||||
return processSqlOffsetFetch( sql, hasFirstRow, hasMaxRows );
|
||||
}
|
||||
}
|
||||
}
|
||||
return processSqlOffsetFetch( sql, hasFirstRow );
|
||||
return processSqlOffsetFetch( sql, hasFirstRow, hasMaxRows );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #processSqlOffsetFetch(String, boolean, boolean)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected String processSqlOffsetFetch(String sql, boolean hasFirstRow) {
|
||||
return processSqlOffsetFetch( sql, hasFirstRow, true );
|
||||
}
|
||||
|
||||
protected String processSqlOffsetFetch(String sql, boolean hasFirstRow, boolean hasMaxRows) {
|
||||
|
||||
final int forUpdateLastIndex = getForUpdateIndex( sql );
|
||||
|
||||
if ( forUpdateLastIndex > -1 ) {
|
||||
return processSql( sql, forUpdateLastIndex, hasFirstRow );
|
||||
return processSql( sql, forUpdateLastIndex, hasFirstRow, hasMaxRows );
|
||||
}
|
||||
|
||||
bindLimitParametersInReverseOrder = false;
|
||||
useMaxForLimit = false;
|
||||
supportOffset = true;
|
||||
|
||||
final int offsetFetchLength;
|
||||
final String offsetFetchString;
|
||||
if ( hasFirstRow ) {
|
||||
if ( hasFirstRow && hasMaxRows ) {
|
||||
offsetFetchString = " offset ? rows fetch next ? rows only";
|
||||
}
|
||||
else if ( hasFirstRow ) {
|
||||
offsetFetchString = " offset ? rows";
|
||||
}
|
||||
else {
|
||||
offsetFetchString = " fetch first ? rows only";
|
||||
}
|
||||
offsetFetchLength = sql.length() + offsetFetchString.length();
|
||||
|
||||
return sql + offsetFetchString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #processSql(String, int, boolean, boolean)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected String processSql(String sql, int forUpdateIndex, boolean hasFirstRow) {
|
||||
return processSql( sql, forUpdateIndex, hasFirstRow, true );
|
||||
}
|
||||
|
||||
protected String processSql(String sql, int forUpdateIndex, boolean hasFirstRow, boolean hasMaxRows) {
|
||||
bindLimitParametersInReverseOrder = true;
|
||||
useMaxForLimit = true;
|
||||
supportOffset = false;
|
||||
|
@ -112,12 +134,18 @@ public class Oracle12LimitHandler extends AbstractLimitHandler {
|
|||
forUpdateClauseLength = forUpdateClause.length() + 1;
|
||||
}
|
||||
|
||||
if ( hasFirstRow ) {
|
||||
if ( hasFirstRow && hasMaxRows ) {
|
||||
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 98 );
|
||||
pagingSelect.append( "select * from (select row_.*,rownum rownum_ from (" );
|
||||
pagingSelect.append( sql );
|
||||
pagingSelect.append( ") row_ where rownum<=?) where rownum_>?" );
|
||||
}
|
||||
else if ( hasFirstRow ) {
|
||||
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 98 );
|
||||
pagingSelect.append( "select * from (" );
|
||||
pagingSelect.append( sql );
|
||||
pagingSelect.append( ") row_ where rownum>?" );
|
||||
}
|
||||
else {
|
||||
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 37 );
|
||||
pagingSelect.append( "select * from (" );
|
||||
|
|
|
@ -8,19 +8,13 @@ package org.hibernate.orm.test.query;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.AbstractHANADialect;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.SpannerDialect;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -34,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
annotatedClasses = { NativeQueryLimitOffsetTest.Person.class }
|
||||
)
|
||||
@SessionFactory
|
||||
@JiraKey("HHH-16020")
|
||||
public class NativeQueryLimitOffsetTest {
|
||||
|
||||
@BeforeEach
|
||||
|
@ -61,7 +56,6 @@ public class NativeQueryLimitOffsetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-16020" )
|
||||
public void testFullLimitOffsetOnNativeQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
@ -77,11 +71,6 @@ public class NativeQueryLimitOffsetTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-16020" )
|
||||
@SkipForDialect( dialectClass = MySQLDialect.class, matchSubTypes = true)
|
||||
@SkipForDialect( dialectClass = OracleDialect.class, majorVersion = 12, minorVersion = 2, matchSubTypes = true)
|
||||
@SkipForDialect( dialectClass = AbstractHANADialect.class, matchSubTypes = true)
|
||||
@SkipForDialect( dialectClass = SpannerDialect.class, matchSubTypes = true)
|
||||
public void testPartialLimitOffsetOnNativeQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
|
Loading…
Reference in New Issue