HHH-18201 Configure NO_PLAIN_PARAMETER rendering mode for timestamps in timestampadd and -diff

This commit is contained in:
Christian Beikov 2024-06-04 14:42:45 +02:00
parent edbece8125
commit dba38f84fc
5 changed files with 65 additions and 13 deletions

View File

@ -1221,11 +1221,8 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
//timestampadd()/timestampdiff() delegated back to the Dialect itself //timestampadd()/timestampdiff() delegated back to the Dialect itself
//since there is a great variety of different ways to emulate them //since there is a great variety of different ways to emulate them
//by default, we don't allow plain parameters for the timestamp argument as most database don't support this
functionContributions.getFunctionRegistry().register( "timestampadd", functionFactory.timestampaddAndDiff( this, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
new TimestampaddFunction( this, typeConfiguration ) );
functionContributions.getFunctionRegistry().register( "timestampdiff",
new TimestampdiffFunction( this, typeConfiguration ) );
functionContributions.getFunctionRegistry().registerAlternateKey( "dateadd", "timestampadd" ); functionContributions.getFunctionRegistry().registerAlternateKey( "dateadd", "timestampadd" );
functionContributions.getFunctionRegistry().registerAlternateKey( "datediff", "timestampdiff" ); functionContributions.getFunctionRegistry().registerAlternateKey( "datediff", "timestampdiff" );

View File

@ -2381,6 +2381,29 @@ public class CommonFunctionFactory {
.register(); .register();
} }
public void timestampaddAndDiff(Dialect dialect, SqlAstNodeRenderingMode timestampRenderingMode) {
functionRegistry.register(
"timestampadd",
new TimestampaddFunction(
dialect,
typeConfiguration,
SqlAstNodeRenderingMode.DEFAULT,
SqlAstNodeRenderingMode.DEFAULT,
timestampRenderingMode
)
);
functionRegistry.register(
"timestampdiff",
new TimestampdiffFunction(
dialect,
typeConfiguration,
SqlAstNodeRenderingMode.DEFAULT,
timestampRenderingMode,
timestampRenderingMode
)
);
}
/** /**
* MySQL style, returns the number of days between two dates * MySQL style, returns the number of days between two dates
*/ */

View File

@ -18,6 +18,7 @@ import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
@ -47,8 +48,13 @@ public class TimestampaddFunction
extends AbstractSqmSelfRenderingFunctionDescriptor { extends AbstractSqmSelfRenderingFunctionDescriptor {
private final Dialect dialect; private final Dialect dialect;
private final SqlAstNodeRenderingMode[] renderingModes;
public TimestampaddFunction(Dialect dialect, TypeConfiguration typeConfiguration) { public TimestampaddFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
this( dialect, typeConfiguration, SqlAstNodeRenderingMode.DEFAULT );
}
public TimestampaddFunction(Dialect dialect, TypeConfiguration typeConfiguration, SqlAstNodeRenderingMode... renderingModes) {
super( super(
"timestampadd", "timestampadd",
new ArgumentTypesValidator( new ArgumentTypesValidator(
@ -59,6 +65,7 @@ public class TimestampaddFunction
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, INTEGER, TEMPORAL ) StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, INTEGER, TEMPORAL )
); );
this.dialect = dialect; this.dialect = dialect;
this.renderingModes = renderingModes;
} }
@Override @Override
@ -78,7 +85,7 @@ public class TimestampaddFunction
PatternRenderer patternRenderer(TemporalUnit unit, Expression interval, Expression to) { PatternRenderer patternRenderer(TemporalUnit unit, Expression interval, Expression to) {
TemporalType temporalType = getSqlTemporalType( to.getExpressionType() ); TemporalType temporalType = getSqlTemporalType( to.getExpressionType() );
IntervalType intervalType = getSqlIntervalType( interval.getExpressionType().getSingleJdbcMapping() ); IntervalType intervalType = getSqlIntervalType( interval.getExpressionType().getSingleJdbcMapping() );
return new PatternRenderer( dialect.timestampaddPattern( unit, temporalType, intervalType ) ); return new PatternRenderer( dialect.timestampaddPattern( unit, temporalType, intervalType ), renderingModes );
} }
// @Override // @Override

View File

@ -26,6 +26,7 @@ import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDurationUnit; import org.hibernate.query.sqm.tree.expression.SqmDurationUnit;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
@ -56,8 +57,13 @@ public class TimestampdiffFunction
extends AbstractSqmSelfRenderingFunctionDescriptor { extends AbstractSqmSelfRenderingFunctionDescriptor {
private final Dialect dialect; private final Dialect dialect;
private final SqlAstNodeRenderingMode[] renderingModes;
public TimestampdiffFunction(Dialect dialect, TypeConfiguration typeConfiguration) { public TimestampdiffFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
this( dialect, typeConfiguration, SqlAstNodeRenderingMode.DEFAULT );
}
public TimestampdiffFunction(Dialect dialect, TypeConfiguration typeConfiguration, SqlAstNodeRenderingMode... renderingModes) {
super( super(
"timestampdiff", "timestampdiff",
new ArgumentTypesValidator( new ArgumentTypesValidator(
@ -71,6 +77,7 @@ public class TimestampdiffFunction
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, TEMPORAL, TEMPORAL ) StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TEMPORAL_UNIT, TEMPORAL, TEMPORAL )
); );
this.dialect = dialect; this.dialect = dialect;
this.renderingModes = renderingModes;
} }
@Override @Override
@ -90,7 +97,7 @@ public class TimestampdiffFunction
private PatternRenderer patternRenderer(TemporalUnit unit, Expression from, Expression to) { private PatternRenderer patternRenderer(TemporalUnit unit, Expression from, Expression to) {
TemporalType lhsTemporalType = getSqlTemporalType( from.getExpressionType() ); TemporalType lhsTemporalType = getSqlTemporalType( from.getExpressionType() );
TemporalType rhsTemporalType = getSqlTemporalType( to.getExpressionType() ); TemporalType rhsTemporalType = getSqlTemporalType( to.getExpressionType() );
return new PatternRenderer( dialect.timestampdiffPattern( unit, lhsTemporalType, rhsTemporalType ) ); return new PatternRenderer( dialect.timestampdiffPattern( unit, lhsTemporalType, rhsTemporalType ), renderingModes );
} }
public SelfRenderingFunctionSqlAstExpression expression( public SelfRenderingFunctionSqlAstExpression expression(

View File

@ -36,7 +36,7 @@ public class PatternRenderer {
private final int[] paramIndexes; private final int[] paramIndexes;
private final int varargParam; private final int varargParam;
private final int maxParamIndex; private final int maxParamIndex;
private final SqlAstNodeRenderingMode argumentRenderingMode; private final SqlAstNodeRenderingMode[] argumentRenderingModes;
public PatternRenderer(String pattern) { public PatternRenderer(String pattern) {
this( pattern, SqlAstNodeRenderingMode.DEFAULT ); this( pattern, SqlAstNodeRenderingMode.DEFAULT );
@ -49,6 +49,16 @@ public class PatternRenderer {
* @param argumentRenderingMode The rendering mode for arguments * @param argumentRenderingMode The rendering mode for arguments
*/ */
public PatternRenderer(String pattern, SqlAstNodeRenderingMode argumentRenderingMode) { public PatternRenderer(String pattern, SqlAstNodeRenderingMode argumentRenderingMode) {
this( pattern, new SqlAstNodeRenderingMode[] { argumentRenderingMode } );
}
/**
* Constructs a template renderer
*
* @param pattern The template
* @param argumentRenderingModes The rendering modes for arguments
*/
public PatternRenderer(String pattern, SqlAstNodeRenderingMode[] argumentRenderingModes) {
final Set<Integer> paramNumbers = new HashSet<>(); final Set<Integer> paramNumbers = new HashSet<>();
final List<String> chunkList = new ArrayList<>(); final List<String> chunkList = new ArrayList<>();
final List<Integer> paramList = new ArrayList<>(); final List<Integer> paramList = new ArrayList<>();
@ -116,7 +126,7 @@ public class PatternRenderer {
paramIndexes[i] = paramList.get( i ); paramIndexes[i] = paramList.get( i );
} }
this.paramIndexes = paramIndexes; this.paramIndexes = paramIndexes;
this.argumentRenderingMode = argumentRenderingMode; this.argumentRenderingModes = argumentRenderingModes;
} }
public boolean hasVarargs() { public boolean hasVarargs() {
@ -185,6 +195,7 @@ public class PatternRenderer {
for ( int i = 0; i < chunks.length; i++ ) { for ( int i = 0; i < chunks.length; i++ ) {
if ( i == varargParam ) { if ( i == varargParam ) {
final SqlAstNodeRenderingMode argumentRenderingMode = getArgumentRenderingMode(varargParam - 1);
for ( int j = i; j < numberOfArguments; j++ ) { for ( int j = i; j < numberOfArguments; j++ ) {
final SqlAstNode arg = args.get( j ); final SqlAstNode arg = args.get( j );
if ( arg != null ) { if ( arg != null ) {
@ -217,11 +228,11 @@ public class PatternRenderer {
filter.accept( translator ); filter.accept( translator );
translator.getCurrentClauseStack().pop(); translator.getCurrentClauseStack().pop();
sqlAppender.appendSql( " then " ); sqlAppender.appendSql( " then " );
translator.render( arg, argumentRenderingMode ); translator.render( arg, getArgumentRenderingMode(index) );
sqlAppender.appendSql( " else null end" ); sqlAppender.appendSql( " else null end" );
} }
else { else {
translator.render( arg, argumentRenderingMode ); translator.render( arg, getArgumentRenderingMode(index) );
} }
} }
} }
@ -233,10 +244,10 @@ public class PatternRenderer {
if ( withinGroup != null && !withinGroup.isEmpty() ) { if ( withinGroup != null && !withinGroup.isEmpty() ) {
translator.getCurrentClauseStack().push( Clause.WITHIN_GROUP ); translator.getCurrentClauseStack().push( Clause.WITHIN_GROUP );
sqlAppender.appendSql( " within group (order by" ); sqlAppender.appendSql( " within group (order by" );
translator.render( withinGroup.get( 0 ), argumentRenderingMode ); translator.render( withinGroup.get( 0 ), getArgumentRenderingMode( 0 ) );
for ( int i = 1; i < withinGroup.size(); i++ ) { for ( int i = 1; i < withinGroup.size(); i++ ) {
sqlAppender.appendSql( SqlAppender.COMMA_SEPARATOR_CHAR ); sqlAppender.appendSql( SqlAppender.COMMA_SEPARATOR_CHAR );
translator.render( withinGroup.get( 0 ), argumentRenderingMode ); translator.render( withinGroup.get( 0 ), getArgumentRenderingMode( 0 ) );
} }
sqlAppender.appendSql( ')' ); sqlAppender.appendSql( ')' );
translator.getCurrentClauseStack().pop(); translator.getCurrentClauseStack().pop();
@ -267,4 +278,11 @@ public class PatternRenderer {
translator.getCurrentClauseStack().pop(); translator.getCurrentClauseStack().pop();
} }
} }
private SqlAstNodeRenderingMode getArgumentRenderingMode(int index) {
if ( index < argumentRenderingModes.length ) {
return argumentRenderingModes[index];
}
return argumentRenderingModes[argumentRenderingModes.length - 1];
}
} }