-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HHH-15191 - Remove support for Derby versions older than 10.14.2.0
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
- Loading branch information
Showing
10 changed files
with
1,384 additions
and
58 deletions.
There are no files selected for viewing
957 changes: 957 additions & 0 deletions
957
...-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java
Large diffs are not rendered by default.
Oops, something went wrong.
299 changes: 299 additions & 0 deletions
299
...y-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacySqlAstTranslator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,299 @@ | ||
/* | ||
* 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.community.dialect; | ||
|
||
import java.util.List; | ||
import java.util.function.Consumer; | ||
|
||
import org.hibernate.engine.spi.SessionFactoryImplementor; | ||
import org.hibernate.query.sqm.BinaryArithmeticOperator; | ||
import org.hibernate.query.sqm.ComparisonOperator; | ||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; | ||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; | ||
import org.hibernate.sql.ast.spi.SqlSelection; | ||
import org.hibernate.sql.ast.tree.Statement; | ||
import org.hibernate.sql.ast.tree.cte.CteContainer; | ||
import org.hibernate.sql.ast.tree.cte.CteStatement; | ||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; | ||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; | ||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; | ||
import org.hibernate.sql.ast.tree.expression.Expression; | ||
import org.hibernate.sql.ast.tree.expression.Literal; | ||
import org.hibernate.sql.ast.tree.expression.SqlTuple; | ||
import org.hibernate.sql.ast.tree.expression.Summarization; | ||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate; | ||
import org.hibernate.sql.ast.tree.predicate.InListPredicate; | ||
import org.hibernate.sql.ast.tree.select.QueryPart; | ||
import org.hibernate.sql.exec.spi.JdbcOperation; | ||
|
||
/** | ||
* A SQL AST translator for Derby. | ||
* | ||
* @author Christian Beikov | ||
*/ | ||
public class DerbyLegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> { | ||
|
||
public DerbyLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { | ||
super( sessionFactory, statement ); | ||
} | ||
|
||
@Override | ||
protected void renderExpressionAsClauseItem(Expression expression) { | ||
expression.accept( this ); | ||
} | ||
|
||
@Override | ||
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) { | ||
final boolean isNegated = booleanExpressionPredicate.isNegated(); | ||
if ( isNegated ) { | ||
appendSql( "not(" ); | ||
} | ||
booleanExpressionPredicate.getExpression().accept( this ); | ||
if ( isNegated ) { | ||
appendSql( CLOSE_PARENTHESIS ); | ||
} | ||
} | ||
|
||
// Derby does not allow CASE expressions where all result arms contain plain parameters. | ||
// At least one result arm must provide some type context for inference, | ||
// so we cast the first result arm if we encounter this condition | ||
|
||
@Override | ||
protected void visitAnsiCaseSearchedExpression( | ||
CaseSearchedExpression caseSearchedExpression, | ||
Consumer<Expression> resultRenderer) { | ||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSearchedExpression ) ) { | ||
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments(); | ||
final Expression firstResult = whenFragments.get( 0 ).getResult(); | ||
super.visitAnsiCaseSearchedExpression( | ||
caseSearchedExpression, | ||
e -> { | ||
if ( e == firstResult ) { | ||
renderCasted( e ); | ||
} | ||
else { | ||
resultRenderer.accept( e ); | ||
} | ||
} | ||
); | ||
} | ||
else { | ||
super.visitAnsiCaseSearchedExpression( caseSearchedExpression, resultRenderer ); | ||
} | ||
} | ||
|
||
@Override | ||
protected void visitAnsiCaseSimpleExpression( | ||
CaseSimpleExpression caseSimpleExpression, | ||
Consumer<Expression> resultRenderer) { | ||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSimpleExpression ) ) { | ||
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments(); | ||
final Expression firstResult = whenFragments.get( 0 ).getResult(); | ||
super.visitAnsiCaseSimpleExpression( | ||
caseSimpleExpression, | ||
e -> { | ||
if ( e == firstResult ) { | ||
renderCasted( e ); | ||
} | ||
else { | ||
resultRenderer.accept( e ); | ||
} | ||
} | ||
); | ||
} | ||
else { | ||
super.visitAnsiCaseSimpleExpression( caseSimpleExpression, resultRenderer ); | ||
} | ||
} | ||
|
||
@Override | ||
protected String getForUpdate() { | ||
return " for update"; | ||
} | ||
|
||
@Override | ||
protected String getForShare(int timeoutMillis) { | ||
return " for read only"; | ||
} | ||
|
||
@Override | ||
protected String getForUpdateWithClause() { | ||
return " with rs"; | ||
} | ||
|
||
@Override | ||
public void visitCteContainer(CteContainer cteContainer) { | ||
if ( cteContainer.isWithRecursive() ) { | ||
throw new IllegalArgumentException( "Recursive CTEs can't be emulated" ); | ||
} | ||
super.visitCteContainer( cteContainer ); | ||
} | ||
|
||
@Override | ||
protected void renderSearchClause(CteStatement cte) { | ||
// Derby does not support this, but it's just a hint anyway | ||
} | ||
|
||
@Override | ||
protected void renderCycleClause(CteStatement cte) { | ||
// Derby does not support this, but it can be emulated | ||
} | ||
|
||
@Override | ||
public void visitOffsetFetchClause(QueryPart queryPart) { | ||
// Derby only supports the OFFSET and FETCH clause with ROWS | ||
assertRowsOnlyFetchClauseType( queryPart ); | ||
if ( supportsOffsetFetchClause() ) { | ||
renderOffsetFetchClause( queryPart, true ); | ||
} | ||
else if ( !getClauseStack().isEmpty() ) { | ||
throw new IllegalArgumentException( "Can't render offset and fetch clause for subquery" ); | ||
} | ||
} | ||
|
||
@Override | ||
protected void renderFetchExpression(Expression fetchExpression) { | ||
if ( supportsParameterOffsetFetchExpression() ) { | ||
super.renderFetchExpression( fetchExpression ); | ||
} | ||
else { | ||
renderExpressionAsLiteral( fetchExpression, getJdbcParameterBindings() ); | ||
} | ||
} | ||
|
||
@Override | ||
protected void renderOffsetExpression(Expression offsetExpression) { | ||
if ( supportsParameterOffsetFetchExpression() ) { | ||
super.renderOffsetExpression( offsetExpression ); | ||
} | ||
else { | ||
renderExpressionAsLiteral( offsetExpression, getJdbcParameterBindings() ); | ||
} | ||
} | ||
|
||
@Override | ||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) { | ||
renderComparisonEmulateIntersect( lhs, operator, rhs ); | ||
} | ||
|
||
@Override | ||
protected void renderSelectExpression(Expression expression) { | ||
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression ); | ||
} | ||
|
||
@Override | ||
protected void renderSelectTupleComparison( | ||
List<SqlSelection> lhsExpressions, | ||
SqlTuple tuple, | ||
ComparisonOperator operator) { | ||
emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true ); | ||
} | ||
|
||
@Override | ||
protected void renderPartitionItem(Expression expression) { | ||
if ( expression instanceof Literal ) { | ||
appendSql( "'0'" ); | ||
} | ||
else if ( expression instanceof Summarization ) { | ||
Summarization summarization = (Summarization) expression; | ||
appendSql( summarization.getKind().sqlText() ); | ||
appendSql( OPEN_PARENTHESIS ); | ||
renderCommaSeparated( summarization.getGroupings() ); | ||
appendSql( CLOSE_PARENTHESIS ); | ||
} | ||
else { | ||
expression.accept( this ); | ||
} | ||
} | ||
|
||
@Override | ||
public void visitInListPredicate(InListPredicate inListPredicate) { | ||
final List<Expression> listExpressions = inListPredicate.getListExpressions(); | ||
if ( listExpressions.isEmpty() ) { | ||
appendSql( "1=0" ); | ||
return; | ||
} | ||
final Expression testExpression = inListPredicate.getTestExpression(); | ||
if ( isParameter( testExpression ) ) { | ||
renderCasted( testExpression ); | ||
if ( inListPredicate.isNegated() ) { | ||
appendSql( " not" ); | ||
} | ||
appendSql( " in(" ); | ||
renderCommaSeparated( listExpressions ); | ||
appendSql( CLOSE_PARENTHESIS ); | ||
} | ||
else { | ||
super.visitInListPredicate( inListPredicate ); | ||
} | ||
} | ||
|
||
@Override | ||
protected boolean supportsRowValueConstructorSyntax() { | ||
return false; | ||
} | ||
|
||
@Override | ||
protected boolean supportsRowValueConstructorSyntaxInInList() { | ||
return false; | ||
} | ||
|
||
@Override | ||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() { | ||
return false; | ||
} | ||
|
||
@Override | ||
protected String getFromDual() { | ||
return " from (values 0) dual"; | ||
} | ||
|
||
@Override | ||
protected String getFromDualForSelectOnly() { | ||
return getFromDual(); | ||
} | ||
|
||
@Override | ||
protected boolean needsRowsToSkip() { | ||
return !supportsOffsetFetchClause(); | ||
} | ||
|
||
@Override | ||
protected boolean needsMaxRows() { | ||
return !supportsOffsetFetchClause(); | ||
} | ||
|
||
private boolean supportsParameterOffsetFetchExpression() { | ||
return getDialect().getVersion().isSameOrAfter( 10, 6 ); | ||
} | ||
|
||
private boolean supportsOffsetFetchClause() { | ||
// Before version 10.5 Derby didn't support OFFSET and FETCH | ||
return getDialect().getVersion().isSameOrAfter( 10, 5 ); | ||
} | ||
|
||
@Override | ||
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { | ||
final BinaryArithmeticOperator operator = arithmeticExpression.getOperator(); | ||
if ( operator == BinaryArithmeticOperator.MODULO ) { | ||
append( "mod" ); | ||
appendSql( OPEN_PARENTHESIS ); | ||
arithmeticExpression.getLeftHandOperand().accept( this ); | ||
appendSql( ',' ); | ||
arithmeticExpression.getRightHandOperand().accept( this ); | ||
appendSql( CLOSE_PARENTHESIS ); | ||
} | ||
else { | ||
appendSql( OPEN_PARENTHESIS ); | ||
render( arithmeticExpression.getLeftHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ); | ||
appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() ); | ||
render( arithmeticExpression.getRightHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ); | ||
appendSql( CLOSE_PARENTHESIS ); | ||
} | ||
} | ||
|
||
} |
93 changes: 93 additions & 0 deletions
93
...ty-dialects/src/test/java/org/hibernate/community/dialect/DerbyLegacyDialectTestCase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* 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.community.dialect; | ||
|
||
import org.hibernate.dialect.DatabaseVersion; | ||
import org.hibernate.query.spi.Limit; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import org.hibernate.testing.TestForIssue; | ||
import org.hibernate.testing.junit4.BaseUnitTestCase; | ||
import org.junit.Test; | ||
|
||
/** | ||
* Testing of patched support for Derby limit and offset queries; see HHH-3972 | ||
* | ||
* @author Evan Leonard | ||
*/ | ||
public class DerbyLegacyDialectTestCase extends BaseUnitTestCase { | ||
|
||
@Test | ||
@TestForIssue( jiraKey = "HHH-3972" ) | ||
public void testInsertLimitClause() { | ||
final int limit = 50; | ||
final String input = "select * from tablename t where t.cat = 5"; | ||
final String expected = "select * from tablename t where t.cat = 5 fetch first " + limit + " rows only"; | ||
|
||
final String actual = new DerbyLegacyDialect( DatabaseVersion.make( 10, 5 ) ).getLimitHandler().processSql( input, toRowSelection( 0, limit ) ); | ||
assertEquals( expected, actual ); | ||
} | ||
|
||
@Test | ||
@TestForIssue( jiraKey = "HHH-3972" ) | ||
public void testInsertLimitWithOffsetClause() { | ||
final int limit = 50; | ||
final int offset = 200; | ||
final String input = "select * from tablename t where t.cat = 5"; | ||
final String expected = "select * from tablename t where t.cat = 5 offset " + offset + " rows fetch next " + limit + " rows only"; | ||
|
||
final String actual = new DerbyLegacyDialect( DatabaseVersion.make( 10, 5 ) ).getLimitHandler().processSql( input, toRowSelection( offset, limit ) ); | ||
assertEquals( expected, actual ); | ||
} | ||
|
||
@Test | ||
@TestForIssue( jiraKey = "HHH-3972" ) | ||
public void testInsertLimitWithForUpdateClause() { | ||
final int limit = 50; | ||
final int offset = 200; | ||
final String input = "select c11 as col1, c12 as col2, c13 as col13 from t1 for update of c11, c13"; | ||
final String expected = "select c11 as col1, c12 as col2, c13 as col13 from t1 offset " + offset | ||
+ " rows fetch next " + limit + " rows only for update of c11, c13"; | ||
|
||
final String actual = new DerbyLegacyDialect( DatabaseVersion.make( 10, 5 ) ).getLimitHandler().processSql( input, toRowSelection( offset, limit ) ); | ||
assertEquals( expected, actual ); | ||
} | ||
|
||
@Test | ||
@TestForIssue( jiraKey = "HHH-3972" ) | ||
public void testInsertLimitWithWithClause() { | ||
final int limit = 50; | ||
final int offset = 200; | ||
final String input = "select c11 as col1, c12 as col2, c13 as col13 from t1 where flight_id between 'AA1111' and 'AA1112' with rr"; | ||
final String expected = "select c11 as col1, c12 as col2, c13 as col13 from t1 where flight_id between 'AA1111' and 'AA1112' offset " + offset | ||
+ " rows fetch next " + limit + " rows only with rr"; | ||
|
||
final String actual = new DerbyLegacyDialect( DatabaseVersion.make( 10, 5 ) ).getLimitHandler().processSql( input, toRowSelection( offset, limit ) ); | ||
assertEquals( expected, actual ); | ||
} | ||
|
||
@Test | ||
@TestForIssue( jiraKey = "HHH-3972" ) | ||
public void testInsertLimitWithForUpdateAndWithClauses() { | ||
final int limit = 50; | ||
final int offset = 200; | ||
final String input = "select c11 as col1, c12 as col2, c13 as col13 from t1 where flight_id between 'AA1111' and 'AA1112' for update of c11,c13 with rr"; | ||
final String expected = "select c11 as col1, c12 as col2, c13 as col13 from t1 where flight_id between 'AA1111' and 'AA1112' offset " + offset | ||
+ " rows fetch next " + limit + " rows only for update of c11,c13 with rr"; | ||
|
||
final String actual = new DerbyLegacyDialect( DatabaseVersion.make( 10, 5 ) ).getLimitHandler().processSql( input, toRowSelection( offset, limit ) ); | ||
assertEquals( expected, actual ); | ||
} | ||
|
||
private Limit toRowSelection(int firstRow, int maxRows) { | ||
Limit selection = new Limit(); | ||
selection.setFirstRow( firstRow ); | ||
selection.setMaxRows( maxRows ); | ||
return selection; | ||
} | ||
} |
Oops, something went wrong.