HHH-17074 Type inference in duration arithmetic is wrong

This commit is contained in:
Christian Beikov 2023-08-14 18:32:57 +02:00
parent 10baf4398a
commit 6a56fc62e5
2 changed files with 33 additions and 2 deletions

View File

@ -6245,6 +6245,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final BinaryArithmeticOperator operator = expression.getOperator(); final BinaryArithmeticOperator operator = expression.getOperator();
final SqmExpression<?> lhs = SqmExpressionHelper.getActualExpression( expression.getLeftHandOperand() ); final SqmExpression<?> lhs = SqmExpressionHelper.getActualExpression( expression.getLeftHandOperand() );
final SqmExpression<?> rhs = SqmExpressionHelper.getActualExpression( expression.getRightHandOperand() ); final SqmExpression<?> rhs = SqmExpressionHelper.getActualExpression( expression.getRightHandOperand() );
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
// we have a date or timestamp somewhere to // we have a date or timestamp somewhere to
// the right of us, so we need to restructure // the right of us, so we need to restructure
@ -6277,7 +6278,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
Expression timestamp = adjustedTimestamp; Expression timestamp = adjustedTimestamp;
SqmExpressible<?> timestampType = adjustedTimestampType; SqmExpressible<?> timestampType = adjustedTimestampType;
inferrableTypeAccessStack.push( () -> determineValueMapping( rhs, fromClauseIndex ) );
adjustedTimestamp = toSqlExpression( lhs.accept( this ) ); adjustedTimestamp = toSqlExpression( lhs.accept( this ) );
inferrableTypeAccessStack.pop();
JdbcMappingContainer type = adjustedTimestamp.getExpressionType(); JdbcMappingContainer type = adjustedTimestamp.getExpressionType();
if ( type instanceof SqmExpressible) { if ( type instanceof SqmExpressible) {
adjustedTimestampType = (SqmExpressible<?>) type; adjustedTimestampType = (SqmExpressible<?>) type;
@ -6293,6 +6296,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
negativeAdjustment = !negativeAdjustment; negativeAdjustment = !negativeAdjustment;
} }
try { try {
inferrableTypeAccessStack.push( () -> determineValueMapping( lhs, fromClauseIndex ) );
final Object result = rhs.accept( this ); final Object result = rhs.accept( this );
if ( result instanceof SqlTupleContainer ) { if ( result instanceof SqlTupleContainer ) {
return result; return result;
@ -6324,6 +6328,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
); );
} }
finally { finally {
inferrableTypeAccessStack.pop();
if ( operator == SUBTRACT ) { if ( operator == SUBTRACT ) {
negativeAdjustment = !negativeAdjustment; negativeAdjustment = !negativeAdjustment;
} }
@ -6342,15 +6347,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// x * (d1 - d2) => x * d1 - x * d2 // x * (d1 - d2) => x * d1 - x * d2
// -x * (d1 + d2) => - x * d1 - x * d2 // -x * (d1 + d2) => - x * d1 - x * d2
// -x * (d1 - d2) => - x * d1 + x * d2 // -x * (d1 - d2) => - x * d1 + x * d2
inferrableTypeAccessStack.push( () -> determineValueMapping( rhs, fromClauseIndex ) );
Expression duration = toSqlExpression( lhs.accept( this ) ); Expression duration = toSqlExpression( lhs.accept( this ) );
inferrableTypeAccessStack.pop();
Expression scale = adjustmentScale; Expression scale = adjustmentScale;
boolean negate = negativeAdjustment; boolean negate = negativeAdjustment;
adjustmentScale = applyScale( duration ); adjustmentScale = applyScale( duration );
negativeAdjustment = false; //was sucked into the scale negativeAdjustment = false; //was sucked into the scale
try { try {
inferrableTypeAccessStack.push( () -> determineValueMapping( lhs, fromClauseIndex ) );
return rhs.accept( this ); return rhs.accept( this );
} }
finally { finally {
inferrableTypeAccessStack.pop();
adjustmentScale = scale; adjustmentScale = scale;
negativeAdjustment = negate; negativeAdjustment = negate;
} }
@ -6379,8 +6388,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final SqmExpression<?> lhs = SqmExpressionHelper.getActualExpression( expression.getLeftHandOperand() ); final SqmExpression<?> lhs = SqmExpressionHelper.getActualExpression( expression.getLeftHandOperand() );
final SqmExpression<?> rhs = SqmExpressionHelper.getActualExpression( expression.getRightHandOperand() ); final SqmExpression<?> rhs = SqmExpressionHelper.getActualExpression( expression.getRightHandOperand() );
Expression left = getActualExpression( cleanly( () -> toSqlExpression( lhs.accept( this ) ) ) ); final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
Expression right = getActualExpression( cleanly( () -> toSqlExpression( rhs.accept( this ) ) ) ); inferrableTypeAccessStack.push( () -> determineValueMapping( rhs, fromClauseIndex ) );
final Expression left = getActualExpression( cleanly( () -> toSqlExpression( lhs.accept( this ) ) ) );
inferrableTypeAccessStack.pop();
inferrableTypeAccessStack.push( () -> determineValueMapping( lhs, fromClauseIndex ) );
final Expression right = getActualExpression( cleanly( () -> toSqlExpression( rhs.accept( this ) ) ) );
inferrableTypeAccessStack.pop();
// The result of timestamp subtraction is always a `Duration`, unless a unit is applied // The result of timestamp subtraction is always a `Duration`, unless a unit is applied
// So use SECOND granularity with fractions as that is what the `DurationJavaType` expects // So use SECOND granularity with fractions as that is what the `DurationJavaType` expects

View File

@ -25,6 +25,7 @@ import org.hibernate.testing.orm.domain.gambit.EntityOfMaps;
import org.hibernate.testing.orm.domain.gambit.SimpleEntity; import org.hibernate.testing.orm.domain.gambit.SimpleEntity;
import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
@ -1406,6 +1407,22 @@ public class FunctionTests {
); );
} }
@Test
@JiraKey("HHH-17074")
public void testDurationArithmeticWithParameters(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertEquals(
1,
session.createQuery( "from EntityOfBasics e where (:date - e.theTimestamp) by day > 1" )
.setParameter( "date", Timestamp.valueOf( "2022-01-01 00:00:00" ) )
.getResultList()
.size()
);
}
);
}
@Test @Test
public void testIntervalDiffExpressions(SessionFactoryScope scope) { public void testIntervalDiffExpressions(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(