HHH-18201 Handle SqmBinaryArithmetic in determineValueMapping

This commit is contained in:
Christian Beikov 2024-06-04 19:51:43 +02:00
parent dba38f84fc
commit cf0e4d4622
3 changed files with 48 additions and 7 deletions

View File

@ -9,6 +9,7 @@ package org.hibernate.dialect;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
@ -510,7 +511,11 @@ public class DerbyDialect extends Dialect {
case NATIVE:
return "{fn timestampadd(sql_tsi_frac_second,mod(bigint(?2),1000000000),{fn timestampadd(sql_tsi_second,bigint((?2)/1000000000),?3)})}";
default:
return "{fn timestampadd(sql_tsi_?1,bigint(?2),?3)}";
final String addExpression = "{fn timestampadd(sql_tsi_?1,bigint(?2),?3)}";
// Since timestampadd will always produce a TIMESTAMP, we have to cast back to the intended type
return temporalType == TemporalType.TIMESTAMP
? addExpression
: "cast(" + addExpression + " as " + temporalType.name().toLowerCase( Locale.ROOT ) + ")" ;
}
}

View File

@ -6010,6 +6010,21 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
}
if ( sqmExpression instanceof SqmBinaryArithmetic<?> ) {
final SqmBinaryArithmetic<?> binaryArithmetic = (SqmBinaryArithmetic<?>) sqmExpression;
if ( binaryArithmetic.getNodeType() == null ) {
final MappingModelExpressible<?> lhs = determineValueMapping( binaryArithmetic.getLeftHandOperand() );
final MappingModelExpressible<?> rhs = determineValueMapping( binaryArithmetic.getRightHandOperand() );
// This cast should be fine since the result of this will be a BasicType
return (MappingModelExpressible<?>) getTypeConfiguration().resolveArithmeticType(
// These casts should be safe, since the only JdbcMapping is BasicType
// which also implements SqmExpressible
lhs == null ? null : (SqmExpressible<?>) lhs.getSingleJdbcMapping(),
rhs == null ? null : (SqmExpressible<?>) rhs.getSingleJdbcMapping()
);
}
}
log.debugf( "Determining mapping-model type for generalized SqmExpression : %s", sqmExpression );
final SqmExpressible<?> nodeType = sqmExpression.getNodeType();
if ( nodeType == null ) {
@ -6536,11 +6551,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
SqmExpression<?> leftOperand = expression.getLeftHandOperand();
SqmExpression<?> rightOperand = expression.getRightHandOperand();
boolean durationToRight = isDuration( rightOperand.getNodeType() );
TypeConfiguration typeConfiguration = getCreationContext().getMappingMetamodel().getTypeConfiguration();
TemporalType temporalTypeToLeft = typeConfiguration.getSqlTemporalType( leftOperand.getNodeType() );
TemporalType temporalTypeToRight = typeConfiguration.getSqlTemporalType( rightOperand.getNodeType() );
boolean temporalTypeSomewhereToLeft = adjustedTimestamp != null || temporalTypeToLeft != null;
// Need to infer the operand types here first to decide how to transform the expression
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
inferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand, fromClauseIndex ) );
final MappingModelExpressible<?> leftOperandType = determineValueMapping( leftOperand );
inferrableTypeAccessStack.pop();
inferrableTypeAccessStack.push( () -> determineValueMapping( leftOperand, fromClauseIndex ) );
final MappingModelExpressible<?> rightOperandType = determineValueMapping( rightOperand );
inferrableTypeAccessStack.pop();
final boolean durationToRight = isDuration( rightOperand.getNodeType() );
final TypeConfiguration typeConfiguration = getCreationContext().getMappingMetamodel().getTypeConfiguration();
final TemporalType temporalTypeToLeft = typeConfiguration.getSqlTemporalType( leftOperandType );
final TemporalType temporalTypeToRight = typeConfiguration.getSqlTemporalType( rightOperandType );
final boolean temporalTypeSomewhereToLeft = adjustedTimestamp != null || temporalTypeToLeft != null;
if ( temporalTypeToLeft != null && durationToRight ) {
if ( adjustmentScale != null || negativeAdjustment ) {
@ -6557,7 +6581,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
else {
// Infer one operand type through the other
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
inferrableTypeAccessStack.push( () -> determineValueMapping( rightOperand, fromClauseIndex ) );
final Expression lhs = toSqlExpression( leftOperand.accept( this ) );
inferrableTypeAccessStack.pop();

View File

@ -19,6 +19,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Date;
@ -49,6 +50,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
@ -821,6 +823,17 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
if ( type instanceof BasicValuedMapping ) {
return getSqlTemporalType( ( (BasicValuedMapping) type ).getJdbcMapping().getJdbcType() );
}
else if ( type instanceof EmbeddableValuedModelPart ) {
// Handle the special embeddables for emulated offset/timezone handling
final Class<?> javaTypeClass = ( (EmbeddableValuedModelPart) type ).getJavaType().getJavaTypeClass();
if ( javaTypeClass == OffsetDateTime.class
|| javaTypeClass == ZonedDateTime.class ) {
return TemporalType.TIMESTAMP;
}
else if ( javaTypeClass == OffsetTime.class ) {
return TemporalType.TIME;
}
}
return null;
}