Skip to content

Commit

Permalink
HHH-14624 Oracle from version 12 started supporting the syntax for pa…
Browse files Browse the repository at this point in the history
…gination
  • Loading branch information
dreab8 authored and Sanne committed Jun 1, 2021
1 parent 2952b60 commit 422b80b
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 3 deletions.
Expand Up @@ -10,6 +10,9 @@
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.Oracle12LimitHandler;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.service.ServiceRegistry;
Expand All @@ -24,6 +27,8 @@
public class Oracle12cDialect extends Oracle10gDialect {
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";

private static final AbstractLimitHandler LIMIT_HANDLER = Oracle12LimitHandler.INSTANCE;

public Oracle12cDialect() {
super();
getDefaultProperties().setProperty( Environment.BATCH_VERSIONED_DATA, "true" );
Expand Down Expand Up @@ -62,4 +67,9 @@ public String getNativeIdentifierGeneratorStrategy() {
public IdentityColumnSupport getIdentityColumnSupport() {
return new Oracle12cIdentityColumnSupport();
}

@Override
public LimitHandler getLimitHandler() {
return LIMIT_HANDLER;
}
}
Expand Up @@ -9,6 +9,7 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;

/**
Expand Down Expand Up @@ -37,13 +38,25 @@ public interface LimitHandler {
/**
* Return processed SQL query.
*
* @param sql the SQL query to process.
* @param sql the SQL query to process.
* @param selection the selection criteria for rows.
*
* @return Query statement with LIMIT clause applied.
*/
String processSql(String sql, RowSelection selection);

/**
* Return processed SQL query.
*
* @param sql the SQL query to process.
* @param queryParameters the queryParameters.
*
* @return Query statement with LIMIT clause applied.
*/
default String processSql(String sql, QueryParameters queryParameters ){
return processSql( sql, queryParameters.getRowSelection() );
}

/**
* Bind parameter values needed by the LIMIT clause before original SELECT statement.
*
Expand Down
@@ -0,0 +1,181 @@
/*
* 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.Locale;

import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;

/**
* A {@link LimitHandler} for databases which support the
* ANSI SQL standard syntax {@code FETCH FIRST m ROWS ONLY}
* and {@code OFFSET n ROWS FETCH NEXT m ROWS ONLY}.
*
* @author Gavin King
*/
public class Oracle12LimitHandler extends AbstractLimitHandler {

public boolean bindLimitParametersInReverseOrder;
public boolean useMaxForLimit;

public static final Oracle12LimitHandler INSTANCE = new Oracle12LimitHandler();

Oracle12LimitHandler() {
}

@Override
public String processSql(String sql, RowSelection selection) {
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
final boolean hasMaxRows = LimitHelper.hasMaxRows( selection );

if ( !hasMaxRows ) {
return sql;
}

return processSql( sql, getForUpdateIndex( sql ), hasFirstRow );
}

@Override
public String processSql(String sql, QueryParameters queryParameters) {
final RowSelection selection = queryParameters.getRowSelection();

final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
final boolean hasMaxRows = LimitHelper.hasMaxRows( selection );

if ( !hasMaxRows ) {
return sql;
}

final LockOptions lockOptions = queryParameters.getLockOptions();
if ( lockOptions != null ) {
final LockMode lockMode = lockOptions.getLockMode();
switch ( lockMode ) {
case UPGRADE:
case PESSIMISTIC_READ:
case PESSIMISTIC_WRITE:
case UPGRADE_NOWAIT:
case FORCE:
case PESSIMISTIC_FORCE_INCREMENT:
case UPGRADE_SKIPLOCKED:
return processSql( sql, selection );
default:
return processSqlOffsetFetch( sql, hasFirstRow );
}
}
return processSqlOffsetFetch( sql, hasFirstRow );
}

private String processSqlOffsetFetch(String sql, boolean hasFirstRow) {

final int forUpdateLastIndex = getForUpdateIndex( sql );

if ( forUpdateLastIndex > -1 ) {
return processSql( sql, forUpdateLastIndex, hasFirstRow );
}

bindLimitParametersInReverseOrder = false;
useMaxForLimit = false;

sql = normalizeStatement( sql );
final int offsetFetchLength;
final String offsetFetchString;
if ( hasFirstRow ) {
offsetFetchString = " offset ? rows fetch next ? rows only";
}
else {
offsetFetchString = " fetch first ? rows only";
}
offsetFetchLength = sql.length() + offsetFetchString.length();

return new StringBuilder( offsetFetchLength ).append( sql ).append( offsetFetchString ).toString();
}

private String processSql(String sql, int forUpdateIndex, boolean hasFirstRow) {
bindLimitParametersInReverseOrder = true;
useMaxForLimit = true;
sql = normalizeStatement( sql );

String forUpdateClause = null;
boolean isForUpdate = false;
if ( forUpdateIndex > -1 ) {
// save 'for update ...' and then remove it
forUpdateClause = sql.substring( forUpdateIndex );
sql = sql.substring( 0, forUpdateIndex - 1 );
isForUpdate = true;
}

final StringBuilder pagingSelect;

final int forUpdateClauseLength;
if ( forUpdateClause == null ) {
forUpdateClauseLength = 0;
}
else {
forUpdateClauseLength = forUpdateClause.length() + 1;
}

if ( hasFirstRow ) {
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 {
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 37 );
pagingSelect.append( "select * from ( " );
pagingSelect.append( sql );
pagingSelect.append( " ) where rownum <= ?" );
}

if ( isForUpdate ) {
pagingSelect.append( " " );
pagingSelect.append( forUpdateClause );
}

return pagingSelect.toString();
}

private String normalizeStatement(String sql) {
return sql.trim().replaceAll( "\\s+", " " );
}

private int getForUpdateIndex(String sql) {
final int forUpdateLastIndex = sql.toLowerCase( Locale.ROOT ).lastIndexOf( "for update" );
// We need to recognize cases like : select a from t where b = 'for update';
final int lastIndexOfQuote = sql.lastIndexOf( "'" );
if ( forUpdateLastIndex > -1 ) {
if ( lastIndexOfQuote == -1 ) {
return forUpdateLastIndex;
}
if ( lastIndexOfQuote > forUpdateLastIndex ) {
return -1;
}
return forUpdateLastIndex;
}
return forUpdateLastIndex;
}

@Override
public final boolean supportsLimit() {
return true;
}

@Override
public boolean bindLimitParametersInReverseOrder() {
return bindLimitParametersInReverseOrder;
}

@Override
public boolean useMaxForLimit() {
return useMaxForLimit;
}


}
Expand Up @@ -2021,7 +2021,7 @@ protected SqlStatementWrapper executeQueryStatement(
final LimitHandler limitHandler = getLimitHandler(
queryParameters.getRowSelection()
);
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );

// Adding locks and comments.
sql = preprocessSQL( sql, queryParameters, getFactory(), afterLoadActions );
Expand Down
Expand Up @@ -149,7 +149,7 @@ protected SqlStatementWrapper executeQueryStatement(
final LimitHandler limitHandler = getLimitHandler(
queryParameters.getRowSelection()
);
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );

// Adding locks and comments.
sql = session.getJdbcServices().getJdbcEnvironment().getDialect()
Expand Down

0 comments on commit 422b80b

Please sign in to comment.