diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index 3704749427..e681e4b7c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -129,6 +129,7 @@ import static org.hibernate.cfg.AvailableSettings.USE_STRUCTURED_CACHE; import static org.hibernate.cfg.AvailableSettings.USE_SUBSELECT_FETCH; import static org.hibernate.cfg.CacheSettings.QUERY_CACHE_LAYOUT; +import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION; import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN; import static org.hibernate.internal.CoreLogging.messageLogger; import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER; @@ -270,6 +271,8 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private final boolean failOnPaginationOverCollectionFetchEnabled; private final boolean inClauseParameterPaddingEnabled; + private final boolean portableIntegerDivisionEnabled; + private final int queryStatisticsMaxSize; @@ -591,11 +594,16 @@ else if ( jdbcTimeZoneValue != null ) { this.defaultCatalog = getString( DEFAULT_CATALOG, configurationSettings ); this.defaultSchema = getString( DEFAULT_SCHEMA, configurationSettings ); - this.inClauseParameterPaddingEnabled = getBoolean( + this.inClauseParameterPaddingEnabled = getBoolean( IN_CLAUSE_PARAMETER_PADDING, configurationSettings ); + this.portableIntegerDivisionEnabled = getBoolean( + PORTABLE_INTEGER_DIVISION, + configurationSettings + ); + this.queryStatisticsMaxSize = getInt( QUERY_STATISTICS_MAX_SIZE, configurationSettings, @@ -1213,6 +1221,11 @@ public boolean inClauseParameterPaddingEnabled() { return this.inClauseParameterPaddingEnabled; } + @Override + public boolean isPortableIntegerDivisionEnabled() { + return portableIntegerDivisionEnabled; + } + @Override public JpaCompliance getJpaCompliance() { return jpaCompliance; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 50aa30d3a9..38948d2c20 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -428,6 +428,11 @@ public boolean inClauseParameterPaddingEnabled() { return delegate.inClauseParameterPaddingEnabled(); } + @Override + public boolean isPortableIntegerDivisionEnabled() { + return delegate.isPortableIntegerDivisionEnabled(); + } + @Override public int getQueryStatisticsMaxSize() { return delegate.getQueryStatisticsMaxSize(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index 24ca7fdcf6..8d71deb4be 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -272,10 +272,21 @@ default String getDefaultSchema() { return null; } + /** + * @see org.hibernate.cfg.AvailableSettings#IN_CLAUSE_PARAMETER_PADDING + */ default boolean inClauseParameterPaddingEnabled() { return false; } + /** + * @see org.hibernate.cfg.AvailableSettings#PORTABLE_INTEGER_DIVISION + */ + @Override + default boolean isPortableIntegerDivisionEnabled() { + return false; + } + default int getQueryStatisticsMaxSize() { return Statistics.DEFAULT_QUERY_STATISTICS_MAX_SIZE; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java index dde3cd9e64..28422dfbaa 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java @@ -18,6 +18,14 @@ * @author Steve Ebersole */ public interface QuerySettings { + /** + * Specifies that division of two integers should produce an integer on all + * databases. By default, integer division in HQL can produce a non-integer + * on Oracle, MySQL, or MariaDB. + * + * @since 6.5 + */ + String PORTABLE_INTEGER_DIVISION = "hibernate.query.hql.portable_integer_division"; /** * Specifies a {@link org.hibernate.query.hql.HqlTranslator} to use for HQL query * translation. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index 357af6b209..2c5be4001f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -17,6 +17,7 @@ import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; @@ -51,6 +52,20 @@ public MariaDBSqlAstTranslator(SessionFactoryImplementor sessionFactory, Stateme this.dialect = (MariaDBDialect) DialectDelegateWrapper.extractRealDialect( super.getDialect() ); } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( " div " ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + else { + super.visitBinaryArithmeticExpression(arithmeticExpression); + } + } + @Override protected void visitInsertSource(InsertSelectStatement statement) { if ( statement.getSourceSelectStatement() != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index a55741a187..c2dbfe0060 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -19,6 +19,7 @@ import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; @@ -101,6 +102,20 @@ private static String getSqlType(CastTarget castTarget, String sqlType, Dialect return sqlType; } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) { + appendSql( OPEN_PARENTHESIS ); + arithmeticExpression.getLeftHandOperand().accept( this ); + appendSql( " div " ); + arithmeticExpression.getRightHandOperand().accept( this ); + appendSql( CLOSE_PARENTHESIS ); + } + else { + super.visitBinaryArithmeticExpression(arithmeticExpression); + } + } + @Override protected void visitInsertSource(InsertSelectStatement statement) { if ( statement.getSourceSelectStatement() != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index e47f84bd49..bf6879f77d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -23,6 +23,7 @@ import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteMaterialization; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.FunctionExpression; @@ -587,6 +588,14 @@ else if ( expression instanceof Summarization ) { } } + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) { + appendSql( "floor" ); + } + super.visitBinaryArithmeticExpression(arithmeticExpression); + } + @Override protected boolean supportsDuplicateSelectItemsInQueryGroup() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgresPlusDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgresPlusDialect.java index d3ab56df73..c49013f2e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgresPlusDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgresPlusDialect.java @@ -17,6 +17,12 @@ import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.TemporalUnit; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; +import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.internal.OptionalTableUpdate; import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation; @@ -114,4 +120,23 @@ public MutationOperation createOptionalTableUpdateOperation( // https://www.enterprisedb.com/docs/migrating/oracle/oracle_epas_comparison/notable_differences/ return new OptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory ); } + + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return new StandardSqlAstTranslatorFactory() { + @Override + protected SqlAstTranslator buildTranslator( + SessionFactoryImplementor sessionFactory, Statement statement) { + return new PostgreSQLSqlAstTranslator<>( sessionFactory, statement ) { + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + if ( isIntegerDivisionEmulationRequired( arithmeticExpression ) ) { + appendSql( "floor" ); + } + super.visitBinaryArithmeticExpression(arithmeticExpression); + } + }; + } + }; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/ExtractFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/ExtractFunction.java index 34204e4fd6..dd4cae71df 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/ExtractFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/ExtractFunction.java @@ -29,7 +29,6 @@ import org.hibernate.sql.ast.tree.expression.ExtractUnit; import org.hibernate.type.BasicType; import org.hibernate.type.spi.TypeConfiguration; -import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType; import java.time.ZoneOffset; import java.util.Collections; @@ -40,6 +39,7 @@ import static org.hibernate.query.sqm.TemporalUnit.*; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL_UNIT; +import static org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME; /** * ANSI SQL-inspired {@code extract()} function, where the date/time fields @@ -96,7 +96,7 @@ protected SelfRenderingSqmFunction generateSqmFunctionExpression( case OFFSET: if ( compositeTemporal ) { final SqmPath offsetPath = ( (SqmPath) originalExpression ).get( - AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME + ZONE_OFFSET_NAME ); return new SelfRenderingSqmFunction<>( this, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/FormatFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/FormatFunction.java index b88037560b..8cce3717cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/FormatFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/FormatFunction.java @@ -15,8 +15,6 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.ReturnableType; import org.hibernate.query.spi.QueryEngine; -import org.hibernate.query.sqm.BinaryArithmeticOperator; -import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor; import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; @@ -29,9 +27,6 @@ import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; -import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators; -import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; -import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.sql.ast.SqlAstTranslator; @@ -40,7 +35,6 @@ import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; -import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.DurationUnit; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Format; @@ -54,8 +48,16 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.DIVIDE_PORTABLE; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.MODULO; +import static org.hibernate.query.sqm.ComparisonOperator.GREATER_THAN_OR_EQUAL; +import static org.hibernate.query.sqm.ComparisonOperator.LESS_THAN; +import static org.hibernate.query.sqm.ComparisonOperator.LESS_THAN_OR_EQUAL; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL; +import static org.hibernate.query.sqm.produce.function.StandardArgumentsValidators.exactly; +import static org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers.invariant; +import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.invariant; /** * A format function with support for composite temporal expressions. @@ -89,10 +91,9 @@ public FormatFunction( TypeConfiguration typeConfiguration) { super( "format", - new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL, STRING ), - StandardFunctionReturnTypeResolvers.invariant( typeConfiguration.getBasicTypeRegistry().resolve( - StandardBasicTypes.STRING ) ), - StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL, STRING ) + new ArgumentTypesValidator( exactly( 2 ), TEMPORAL, STRING ), + invariant( typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.STRING ) ), + invariant( typeConfiguration, TEMPORAL, STRING ) ); this.nativeFunctionName = nativeFunctionName; this.reversedArguments = reversedArguments; @@ -260,8 +261,6 @@ public Expression convertToSqlAst(SqmToSqlAstConverter walker) { "substring", 3 ); - final AbstractSqmSelfRenderingFunctionDescriptor floorFunction = getFunction( walker, "floor" ); - final AbstractSqmSelfRenderingFunctionDescriptor castFunction = getFunction( walker, "cast" ); final BasicType stringType = typeConfiguration.getBasicTypeRegistry() .resolve( StandardBasicTypes.STRING ); final Dialect dialect = walker.getCreationContext() @@ -344,8 +343,6 @@ public Expression convertToSqlAst(SqmToSqlAstConverter walker) { createSmallOffset( concatFunction, substringFunction, - floorFunction, - castFunction, stringType, integerType, offsetExpression @@ -365,8 +362,6 @@ public Expression convertToSqlAst(SqmToSqlAstConverter walker) { createMediumOffset( concatFunction, substringFunction, - floorFunction, - castFunction, stringType, integerType, offsetExpression @@ -382,8 +377,6 @@ public Expression convertToSqlAst(SqmToSqlAstConverter walker) { formatExpression, createFullOffset( concatFunction, - floorFunction, - castFunction, stringType, integerType, offsetExpression @@ -491,8 +484,9 @@ private AbstractSqmSelfRenderingFunctionDescriptor getFunction(SqmToSqlAstConver .getSqmFunctionRegistry() .findFunctionDescriptor( name ); if ( functionDescriptor instanceof MultipatternSqmFunctionDescriptor ) { - return (AbstractSqmSelfRenderingFunctionDescriptor) ( (MultipatternSqmFunctionDescriptor) functionDescriptor ) - .getFunction( argumentCount ); + return (AbstractSqmSelfRenderingFunctionDescriptor) + ( (MultipatternSqmFunctionDescriptor) functionDescriptor ) + .getFunction( argumentCount ); } return (AbstractSqmSelfRenderingFunctionDescriptor) functionDescriptor; } @@ -519,8 +513,6 @@ private SqlAstNode getOffsetAdjusted( private Expression createFullOffset( AbstractSqmSelfRenderingFunctionDescriptor concatFunction, - AbstractSqmSelfRenderingFunctionDescriptor floorFunction, - AbstractSqmSelfRenderingFunctionDescriptor castFunction, BasicType stringType, BasicType integerType, Expression offsetExpression) { @@ -529,63 +521,18 @@ private Expression createFullOffset( } else { // ZoneOffset as seconds - final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression( stringType ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN_OR_EQUAL, - new QueryLiteral<>( - -36000, - integerType - ) - ), - new QueryLiteral<>( "-", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN, - new QueryLiteral<>( - 0, - integerType - ) - ), - new QueryLiteral<>( "-0", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.GREATER_THAN_OR_EQUAL, - new QueryLiteral<>( - 36000, - integerType - ) - ), - new QueryLiteral<>( "+", stringType ) - ) - ); - caseSearchedExpression.otherwise( new QueryLiteral<>( "+0", stringType ) ); - final Expression hours = getHours( floorFunction, castFunction, integerType, offsetExpression ); - final Expression minutes = getMinutes( floorFunction, castFunction, integerType, offsetExpression ); + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); + final Expression hours = getHours( integerType, offsetExpression ); + final Expression minutes = getMinutes( integerType, offsetExpression ); final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); minuteStart.getWhenFragments().add( new CaseSearchedExpression.WhenFragment( new BetweenPredicate( minutes, - new QueryLiteral<>( - -9, - integerType - ), - new QueryLiteral<>( - 9, - integerType - ), + new QueryLiteral<>( -9, integerType ), + new QueryLiteral<>( 9, integerType ), false, null ), @@ -610,8 +557,6 @@ private Expression createFullOffset( private Expression createMediumOffset( AbstractSqmSelfRenderingFunctionDescriptor concatFunction, AbstractSqmSelfRenderingFunctionDescriptor substringFunction, - AbstractSqmSelfRenderingFunctionDescriptor floorFunction, - AbstractSqmSelfRenderingFunctionDescriptor castFunction, BasicType stringType, BasicType integerType, Expression offsetExpression) { @@ -622,8 +567,6 @@ private Expression createMediumOffset( createSmallOffset( concatFunction, substringFunction, - floorFunction, - castFunction, stringType, integerType, offsetExpression @@ -643,64 +586,19 @@ private Expression createMediumOffset( } else { // ZoneOffset as seconds - final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression( stringType ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN_OR_EQUAL, - new QueryLiteral<>( - -36000, - integerType - ) - ), - new QueryLiteral<>( "-", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN, - new QueryLiteral<>( - 0, - integerType - ) - ), - new QueryLiteral<>( "-0", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.GREATER_THAN_OR_EQUAL, - new QueryLiteral<>( - 36000, - integerType - ) - ), - new QueryLiteral<>( "+", stringType ) - ) - ); - caseSearchedExpression.otherwise( new QueryLiteral<>( "+0", stringType ) ); + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); - final Expression hours = getHours( floorFunction, castFunction, integerType, offsetExpression ); - final Expression minutes = getMinutes( floorFunction, castFunction, integerType, offsetExpression ); + final Expression hours = getHours( integerType, offsetExpression ); + final Expression minutes = getMinutes( integerType, offsetExpression ); final CaseSearchedExpression minuteStart = new CaseSearchedExpression( stringType ); minuteStart.getWhenFragments().add( new CaseSearchedExpression.WhenFragment( new BetweenPredicate( minutes, - new QueryLiteral<>( - -9, - integerType - ), - new QueryLiteral<>( - 9, - integerType - ), + new QueryLiteral<>( -9, integerType ), + new QueryLiteral<>( 9, integerType ), false, null ), @@ -725,8 +623,6 @@ private Expression createMediumOffset( private Expression createSmallOffset( AbstractSqmSelfRenderingFunctionDescriptor concatFunction, AbstractSqmSelfRenderingFunctionDescriptor substringFunction, - AbstractSqmSelfRenderingFunctionDescriptor floorFunction, - AbstractSqmSelfRenderingFunctionDescriptor castFunction, BasicType stringType, BasicType integerType, Expression offsetExpression) { @@ -745,48 +641,9 @@ private Expression createSmallOffset( } else { // ZoneOffset as seconds - final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression( stringType ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN_OR_EQUAL, - new QueryLiteral<>( - -36000, - integerType - ) - ), - new QueryLiteral<>( "-", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.LESS_THAN, - new QueryLiteral<>( - 0, - integerType - ) - ), - new QueryLiteral<>( "-0", stringType ) - ) - ); - caseSearchedExpression.getWhenFragments().add( - new CaseSearchedExpression.WhenFragment( - new ComparisonPredicate( - offsetExpression, - ComparisonOperator.GREATER_THAN_OR_EQUAL, - new QueryLiteral<>( - 36000, - integerType - ) - ), - new QueryLiteral<>( "+", stringType ) - ) - ); - caseSearchedExpression.otherwise( new QueryLiteral<>( "+0", stringType ) ); - final Expression hours = getHours( floorFunction, castFunction, integerType, offsetExpression ); + final CaseSearchedExpression caseSearchedExpression = + zoneOffsetSeconds( stringType, integerType, offsetExpression ); + final Expression hours = getHours( integerType, offsetExpression ); return concat( concatFunction, stringType, caseSearchedExpression, hours ); } } @@ -844,7 +701,8 @@ else if ( expression instanceof SelfRenderingFunctionSqlAstExpression } else if ( expression2 instanceof SelfRenderingFunctionSqlAstExpression && "concat".equals( ( (SelfRenderingFunctionSqlAstExpression) expression2 ).getFunctionName() ) ) { - List list = (List) ( (SelfRenderingFunctionSqlAstExpression) expression2 ).getArguments(); + final List list = (List) + ( (SelfRenderingFunctionSqlAstExpression) expression2 ).getArguments(); final SqlAstNode firstOperand = list.get( 0 ); if ( expression instanceof QueryLiteral && firstOperand instanceof QueryLiteral ) { list.set( @@ -883,68 +741,84 @@ else if ( expression instanceof QueryLiteral && expression2 instanceof QueryL } private Expression getHours( - AbstractSqmSelfRenderingFunctionDescriptor floorFunction, - AbstractSqmSelfRenderingFunctionDescriptor castFunction, BasicType integerType, Expression offsetExpression) { - return new SelfRenderingFunctionSqlAstExpression( + return /*new SelfRenderingFunctionSqlAstExpression( "cast", castFunction, - List.of( - new SelfRenderingFunctionSqlAstExpression( - "floor", - floorFunction, - List.of( - new BinaryArithmeticExpression( - offsetExpression, - BinaryArithmeticOperator.DIVIDE, - new QueryLiteral<>( 3600, integerType ), - integerType - ) - ), - integerType, + List.of(*/ + new BinaryArithmeticExpression( + offsetExpression, + DIVIDE_PORTABLE, + new QueryLiteral<>( 3600, integerType ), integerType - ), + )/*, new CastTarget( integerType ) ), integerType, integerType - ); + )*/; } private Expression getMinutes( - AbstractSqmSelfRenderingFunctionDescriptor floorFunction, - AbstractSqmSelfRenderingFunctionDescriptor castFunction, BasicType integerType, Expression offsetExpression){ - return new SelfRenderingFunctionSqlAstExpression( + return /*new SelfRenderingFunctionSqlAstExpression( "cast", castFunction, - List.of( - new SelfRenderingFunctionSqlAstExpression( - "floor", - floorFunction, - List.of( - new BinaryArithmeticExpression( - new BinaryArithmeticExpression( - offsetExpression, - BinaryArithmeticOperator.MODULO, - new QueryLiteral<>( 3600, integerType ), - integerType - ), - BinaryArithmeticOperator.DIVIDE, - new QueryLiteral<>( 60, integerType ), - integerType - ) + List.of(*/ + new BinaryArithmeticExpression( + new BinaryArithmeticExpression( + offsetExpression, + MODULO, + new QueryLiteral<>( 3600, integerType ), + integerType ), - integerType, + DIVIDE_PORTABLE, + new QueryLiteral<>( 60, integerType ), integerType - ), + )/*, new CastTarget( integerType ) ), integerType, integerType - ); + )*/; } } + + private static CaseSearchedExpression zoneOffsetSeconds(BasicType stringType, BasicType integerType, Expression offsetExpression) { + final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression(stringType); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + LESS_THAN_OR_EQUAL, + new QueryLiteral<>( -36000, integerType) + ), + new QueryLiteral<>( "-", stringType) + ) + ); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + LESS_THAN, + new QueryLiteral<>( 0, integerType) + ), + new QueryLiteral<>( "-0", stringType) + ) + ); + caseSearchedExpression.getWhenFragments().add( + new CaseSearchedExpression.WhenFragment( + new ComparisonPredicate( + offsetExpression, + GREATER_THAN_OR_EQUAL, + new QueryLiteral<>( 36000, integerType) + ), + new QueryLiteral<>( "+", stringType) + ) + ); + caseSearchedExpression.otherwise( new QueryLiteral<>( "+0", stringType) ); + return caseSearchedExpression; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/IntegralTimestampaddFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/IntegralTimestampaddFunction.java index b9988deac6..33ffccd375 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/IntegralTimestampaddFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/IntegralTimestampaddFunction.java @@ -9,7 +9,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.query.ReturnableType; -import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.TemporalUnit; import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; import org.hibernate.sql.ast.SqlAstTranslator; @@ -28,6 +27,9 @@ import java.util.Arrays; import java.util.List; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.DIVIDE; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.MULTIPLY; + /** * Used in place of {@link TimestampaddFunction} for databases which don't * support fractional seconds in the {@code timestampadd()} function. @@ -110,11 +112,10 @@ private Expression convertedArgument(DurationUnit field, TemporalUnit unit, Expr ? magnitude : new BinaryArithmeticExpression( magnitude, - conversionFactor.charAt(0) == '*' - ? BinaryArithmeticOperator.MULTIPLY - : BinaryArithmeticOperator.DIVIDE, + conversionFactor.charAt(0) == '*' ? MULTIPLY : DIVIDE, new QueryLiteral<>( - expressionType.getExpressibleJavaType().fromString( conversionFactor.substring(1) ), + expressionType.getExpressibleJavaType() + .fromString( conversionFactor.substring(1) ), expressionType ), expressionType diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 3733451d6b..a9333639fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -888,7 +888,7 @@ public Object visitCte(HqlParser.CteContext ctx) { } private void applyCycleClause(JpaCteCriteria cteDefinition, HqlParser.CycleClauseContext ctx) { - final HqlParser.CteAttributesContext attributesContext = (HqlParser.CteAttributesContext) ctx.getChild( 1 ); + final HqlParser.CteAttributesContext attributesContext = ctx.cteAttributes(); final String cycleMarkAttributeName = visitIdentifier( (HqlParser.IdentifierContext) ctx.getChild( 3 ) ); final List cycleAttributes = new ArrayList<>( ( attributesContext.getChildCount() + 1 ) >> 1 ); final List children = attributesContext.children; @@ -948,14 +948,14 @@ private void applySearchClause(JpaCteCriteria cteDefinition, HqlParser.Search else { kind = CteSearchClauseKind.DEPTH_FIRST; } - final String searchAttributeName = visitIdentifier( (HqlParser.IdentifierContext) ctx.getChild( ctx.getChildCount() - 1 ) ); - final HqlParser.SearchSpecificationsContext searchCtx = (HqlParser.SearchSpecificationsContext) ctx.getChild( 4 ); + final String searchAttributeName = visitIdentifier( ctx.identifier() ); + final HqlParser.SearchSpecificationsContext searchCtx = ctx.searchSpecifications(); final List searchOrders = new ArrayList<>( ( searchCtx.getChildCount() + 1 ) >> 1 ); final List children = searchCtx.children; final JpaCteCriteriaType type = cteDefinition.getType(); for ( int i = 0; i < children.size(); i += 2 ) { final HqlParser.SearchSpecificationContext specCtx = (HqlParser.SearchSpecificationContext) children.get( i ); - final String attributeName = visitIdentifier( (HqlParser.IdentifierContext) specCtx.getChild( 0 ) ); + final String attributeName = visitIdentifier( specCtx.identifier() ); final JpaCteCriteriaAttribute attribute = type.getAttribute( attributeName ); if ( attribute == null ) { throw new SemanticException( @@ -972,7 +972,7 @@ private void applySearchClause(JpaCteCriteria cteDefinition, HqlParser.Search int index = 1; if ( index < specCtx.getChildCount() ) { if ( specCtx.getChild( index ) instanceof HqlParser.SortDirectionContext ) { - final HqlParser.SortDirectionContext sortCtx = (HqlParser.SortDirectionContext) specCtx.getChild( index ); + final HqlParser.SortDirectionContext sortCtx = specCtx.sortDirection(); switch ( ( (TerminalNode) sortCtx.getChild( 0 ) ).getSymbol().getType() ) { case HqlParser.ASC: sortOrder = SortDirection.ASCENDING; @@ -986,7 +986,7 @@ private void applySearchClause(JpaCteCriteria cteDefinition, HqlParser.Search index++; } if ( index < specCtx.getChildCount() ) { - final HqlParser.NullsPrecedenceContext nullsPrecedenceContext = (HqlParser.NullsPrecedenceContext) specCtx.getChild( index ); + final HqlParser.NullsPrecedenceContext nullsPrecedenceContext = specCtx.nullsPrecedence(); switch ( ( (TerminalNode) nullsPrecedenceContext.getChild( 1 ) ).getSymbol().getType() ) { case HqlParser.FIRST: nullPrecedence = NullPrecedence.FIRST; @@ -999,13 +999,7 @@ private void applySearchClause(JpaCteCriteria cteDefinition, HqlParser.Search } } } - searchOrders.add( - creationContext.getNodeBuilder().search( - attribute, - sortOrder, - nullPrecedence - ) - ); + searchOrders.add( creationContext.getNodeBuilder().search( attribute, sortOrder, nullPrecedence ) ); } cteDefinition.search( kind, searchAttributeName, searchOrders ); } @@ -1023,7 +1017,7 @@ public SqmQueryPart visitSimpleQueryGroup(HqlParser.SimpleQueryGroupContext c public SqmQueryPart visitQueryOrderExpression(HqlParser.QueryOrderExpressionContext ctx) { final SqmQuerySpec sqmQuerySpec = currentQuerySpec(); final SqmFromClause fromClause = buildInferredFromClause(null); - sqmQuerySpec.setFromClause(fromClause); + sqmQuerySpec.setFromClause( fromClause ); sqmQuerySpec.setSelectClause( buildInferredSelectClause( fromClause ) ); visitQueryOrder( sqmQuerySpec, ctx.queryOrder() ); return sqmQuerySpec; @@ -3050,7 +3044,9 @@ public Object visitMultiplicativeOperator(HqlParser.MultiplicativeOperatorContex case HqlParser.ASTERISK: return BinaryArithmeticOperator.MULTIPLY; case HqlParser.SLASH: - return BinaryArithmeticOperator.DIVIDE; + return this.creationOptions.isPortableIntegerDivisionEnabled() + ? BinaryArithmeticOperator.DIVIDE_PORTABLE + : BinaryArithmeticOperator.DIVIDE; case HqlParser.PERCENT_OP: return BinaryArithmeticOperator.MODULO; default: diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmCreationOptions.java b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmCreationOptions.java index 8b9b80d96c..9f8b64dc69 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmCreationOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/SqmCreationOptions.java @@ -22,5 +22,14 @@ public interface SqmCreationOptions { * * @see StrictJpaComplianceViolation */ - boolean useStrictJpaCompliance(); + default boolean useStrictJpaCompliance() { + return false; + } + + /** + * @see org.hibernate.cfg.AvailableSettings#PORTABLE_INTEGER_DIVISION + */ + default boolean isPortableIntegerDivisionEnabled() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryEngineImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryEngineImpl.java index b35712aa1b..9ff6c595a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryEngineImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryEngineImpl.java @@ -172,7 +172,7 @@ else if ( dialect.getSqmTranslatorFactory() != null ) { private static List sortedFunctionContributors(ServiceRegistry serviceRegistry) { Collection functionContributors = - serviceRegistry.getService(ClassLoaderService.class) + serviceRegistry.requireService(ClassLoaderService.class) .loadJavaServices(FunctionContributor.class); List contributors = new ArrayList<>( functionContributors ); contributors.sort( diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java index eae97d9fc6..8a4dbb2e22 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java @@ -72,4 +72,10 @@ public interface QueryEngineOptions { JpaCompliance getJpaCompliance(); ValueHandlingMode getCriteriaValueHandlingMode(); + + /** + * @see org.hibernate.cfg.AvailableSettings#PORTABLE_INTEGER_DIVISION + */ + boolean isPortableIntegerDivisionEnabled(); + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/BinaryArithmeticOperator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/BinaryArithmeticOperator.java index 37c715f001..3f3b985685 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/BinaryArithmeticOperator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/BinaryArithmeticOperator.java @@ -85,6 +85,25 @@ public char getOperatorSqlText() { } }, + /** + * "Portable" division, that is, true integer division when the + * operands are integers. + * + * @see org.hibernate.cfg.AvailableSettings#PORTABLE_INTEGER_DIVISION + * @see org.hibernate.query.spi.QueryEngineOptions#isPortableIntegerDivisionEnabled() + */ + DIVIDE_PORTABLE { + @Override + public String toLoggableText(String lhs, String rhs) { + return standardToLoggableText( lhs, this, rhs ); + } + + @Override + public char getOperatorSqlText() { + return '/'; + } + }, + ; public abstract String toLoggableText(String lhs, String rhs); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCreationOptionsStandard.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCreationOptionsStandard.java index e854b22129..fb3969228e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCreationOptionsStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCreationOptionsStandard.java @@ -23,4 +23,9 @@ public SqmCreationOptionsStandard(QueryEngineOptions queryEngineOptions) { public boolean useStrictJpaCompliance() { return queryEngineOptions.getJpaCompliance().isJpaQueryComplianceEnabled(); } + + @Override + public boolean isPortableIntegerDivisionEnabled() { + return queryEngineOptions.isPortableIntegerDivisionEnabled(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index d1d3e9aa67..f9f30ee1ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -50,7 +50,6 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.MappingModelExpressible; @@ -64,7 +63,6 @@ import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.ReturnableType; import org.hibernate.query.SortDirection; -import org.hibernate.query.results.TableGroupImpl; import org.hibernate.query.spi.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.BinaryArithmeticOperator; @@ -81,7 +79,6 @@ import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression; import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; -import org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation; import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.query.sqm.tree.expression.Conversion; @@ -227,6 +224,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.query.sqm.BinaryArithmeticOperator.DIVIDE_PORTABLE; import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND; import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import static org.hibernate.sql.results.graph.DomainResultGraphPrinter.logDomainResultGraph; @@ -1318,6 +1316,16 @@ protected void visitInsertStatementOnly(InsertSelectStatement statement) { visitReturningColumns( statement.getReturningColumns() ); } + protected boolean isIntegerDivisionEmulationRequired(BinaryArithmeticExpression expression) { + return expression.getOperator() == DIVIDE_PORTABLE + && jdbcType( expression.getLeftHandOperand() ).isInteger() + && jdbcType( expression.getRightHandOperand() ).isInteger(); + } + + private JdbcType jdbcType(Expression expression) { + return expression.getExpressionType().getSingleJdbcMapping().getJdbcType(); + } + protected void visitInsertSource(InsertSelectStatement statement) { if ( statement.getSourceSelectStatement() != null ) { statement.getSourceSelectStatement().accept( this ); @@ -7149,8 +7157,7 @@ public void visitSqlSelectionExpression(SqlSelectionExpression expression) { @Override public void visitEntityTypeLiteral(EntityTypeLiteral expression) { - final EntityMappingType entityMapping = expression.getEntityTypeDescriptor(); - appendSql( entityMapping.getDiscriminatorSQLValue() ); + appendSql( expression.getEntityTypeDescriptor().getDiscriminatorSQLValue() ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 6c3e770927..7dc1bcb22b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -61,9 +61,11 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isOneOf; +import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION; import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java new file mode 100644 index 0000000000..96f60ef21f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/IntegerDivisionTest.java @@ -0,0 +1,27 @@ +package org.hibernate.orm.test.query.hql; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@DomainModel +@SessionFactory +@ServiceRegistry(settings = @Setting(name = PORTABLE_INTEGER_DIVISION, value = "true")) +public class IntegerDivisionTest { + @Test + public void testIntegerDivision(SessionFactoryScope scope) { + scope.inTransaction(s -> { + assertFalse( s.createQuery("select 1 where 1/2 = 0 and 4/3 = 1", Integer.class) + .getResultList().isEmpty() ); + assertEquals( 1, s.createQuery("select 4/3", Integer.class) + .getSingleResult() ); + }); + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java index da2aa8a44d..d185934d74 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java @@ -86,6 +86,7 @@ import org.hibernate.query.criteria.ValueHandlingMode; import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.hql.internal.StandardHqlTranslator; +import org.hibernate.query.hql.spi.SqmCreationOptions; import org.hibernate.query.internal.NamedObjectRepositoryImpl; import org.hibernate.query.internal.QueryInterpretationCacheDisabledImpl; import org.hibernate.query.named.NamedObjectRepository; @@ -478,7 +479,7 @@ public NamedObjectRepository getNamedObjectRepository() { @Override public HqlTranslator getHqlTranslator() { - return new StandardHqlTranslator(MockSessionFactory.this, () -> false); + return new StandardHqlTranslator(MockSessionFactory.this, new SqmCreationOptions() {}); } @Override diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/Validation.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/Validation.java index 5ff23c4584..6a3a9f9bab 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/Validation.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/Validation.java @@ -19,6 +19,7 @@ import org.hibernate.grammars.hql.HqlParser; import org.hibernate.query.hql.internal.HqlParseTreeBuilder; import org.hibernate.query.hql.internal.SemanticQueryBuilder; +import org.hibernate.query.hql.spi.SqmCreationOptions; import org.hibernate.query.sqm.EntityTypeException; import org.hibernate.query.sqm.PathElementException; import org.hibernate.query.sqm.TerminalPathException; @@ -99,6 +100,9 @@ public interface Handler extends ANTLRErrorListener { return null; } + private static final SqmCreationOptions CREATION_OPTIONS = new SqmCreationOptions() { + }; + private static SemanticQueryBuilder createSemanticQueryBuilder( @Nullable TypeMirror returnType, String hql, SessionFactoryImplementor factory) { if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) { @@ -107,11 +111,11 @@ private static SemanticQueryBuilder createSemanticQueryBuilder( final String typeName = typeElement.getQualifiedName().toString(); final String shortName = typeElement.getSimpleName().toString(); return isEntity( typeElement ) - ? new SemanticQueryBuilder<>( typeName, shortName, getEntityName(typeElement), () -> false, factory, hql ) - : new SemanticQueryBuilder<>( typeName, shortName, Object[].class, () -> false, factory, hql ); + ? new SemanticQueryBuilder<>( typeName, shortName, getEntityName(typeElement), CREATION_OPTIONS, factory, hql ) + : new SemanticQueryBuilder<>( typeName, shortName, Object[].class, CREATION_OPTIONS, factory, hql ); } else { - return new SemanticQueryBuilder<>( Object[].class, () -> false, factory, hql ); + return new SemanticQueryBuilder<>( Object[].class, CREATION_OPTIONS, factory, hql ); } }