Introduce FunctionExpression in SQL AST and remove shallowness handling in sqm to sql converter

This commit is contained in:
Christian Beikov 2021-03-10 15:24:34 +01:00
parent 7934625688
commit 682678fbe5
6 changed files with 111 additions and 131 deletions

View File

@ -90,6 +90,7 @@ public class TimestampaddFunction
SqlAstNode... sqlAstArguments) { SqlAstNode... sqlAstArguments) {
Expression to = (Expression) sqlAstArguments[2]; Expression to = (Expression) sqlAstArguments[2];
return new SelfRenderingFunctionSqlAstExpression( return new SelfRenderingFunctionSqlAstExpression(
getName(),
this::render, this::render,
asList( sqlAstArguments ), asList( sqlAstArguments ),
impliedResultType, impliedResultType,

View File

@ -95,6 +95,7 @@ public class TimestampdiffFunction
SqlAstNode... sqlAstArguments) { SqlAstNode... sqlAstArguments) {
DurationUnit field = (DurationUnit) sqlAstArguments[0]; DurationUnit field = (DurationUnit) sqlAstArguments[0];
return new SelfRenderingFunctionSqlAstExpression( return new SelfRenderingFunctionSqlAstExpression(
getName(),
this::render, this::render,
asList( sqlAstArguments ), asList( sqlAstArguments ),
impliedResultType, impliedResultType,

View File

@ -22,6 +22,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -40,17 +41,20 @@ import java.util.List;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SelfRenderingFunctionSqlAstExpression public class SelfRenderingFunctionSqlAstExpression
implements SelfRenderingExpression, Selectable, SqlExpressable, DomainResultProducer { implements SelfRenderingExpression, Selectable, SqlExpressable, DomainResultProducer, FunctionExpression {
private final String functionName;
private final FunctionRenderingSupport renderer; private final FunctionRenderingSupport renderer;
private final List<SqlAstNode> sqlAstArguments; private final List<SqlAstNode> sqlAstArguments;
private final AllowableFunctionReturnType<?> type; private final AllowableFunctionReturnType<?> type;
private final MappingModelExpressable<?> expressable; private final MappingModelExpressable<?> expressable;
public SelfRenderingFunctionSqlAstExpression( public SelfRenderingFunctionSqlAstExpression(
String functionName,
FunctionRenderingSupport renderer, FunctionRenderingSupport renderer,
List<SqlAstNode> sqlAstArguments, List<SqlAstNode> sqlAstArguments,
AllowableFunctionReturnType<?> type, AllowableFunctionReturnType<?> type,
MappingModelExpressable<?> expressable) { MappingModelExpressable<?> expressable) {
this.functionName = functionName;
this.renderer = renderer; this.renderer = renderer;
this.sqlAstArguments = sqlAstArguments; this.sqlAstArguments = sqlAstArguments;
this.type = type; this.type = type;
@ -58,6 +62,16 @@ public class SelfRenderingFunctionSqlAstExpression
this.expressable = expressable; this.expressable = expressable;
} }
@Override
public String getFunctionName() {
return functionName;
}
@Override
public List<SqlAstNode> getArguments() {
return sqlAstArguments;
}
@Override @Override
public MappingModelExpressable getExpressionType() { public MappingModelExpressable getExpressionType() {
return expressable; return expressable;

View File

@ -31,9 +31,7 @@ import static java.util.Collections.emptyList;
public class SelfRenderingSqmFunction<T> extends SqmFunction<T> { public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
private final AllowableFunctionReturnType<T> impliedResultType; private final AllowableFunctionReturnType<T> impliedResultType;
private final FunctionReturnTypeResolver returnTypeResolver; private final FunctionReturnTypeResolver returnTypeResolver;
private final String name;
private final FunctionRenderingSupport renderingSupport; private final FunctionRenderingSupport renderingSupport;
private final List<SqmTypedNode<?>> arguments;
private AllowableFunctionReturnType<?> resultType; private AllowableFunctionReturnType<?> resultType;
public SelfRenderingSqmFunction( public SelfRenderingSqmFunction(
@ -44,41 +42,38 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
FunctionReturnTypeResolver returnTypeResolver, FunctionReturnTypeResolver returnTypeResolver,
NodeBuilder nodeBuilder, NodeBuilder nodeBuilder,
String name) { String name) {
super( name, descriptor, impliedResultType, nodeBuilder ); super( name, descriptor, impliedResultType, arguments, nodeBuilder );
this.renderingSupport = renderingSupport; this.renderingSupport = renderingSupport;
this.arguments = arguments;
this.impliedResultType = impliedResultType; this.impliedResultType = impliedResultType;
this.returnTypeResolver = returnTypeResolver; this.returnTypeResolver = returnTypeResolver;
this.name = name;
}
public List<SqmTypedNode<?>> getArguments() {
return arguments;
} }
public FunctionRenderingSupport getRenderingSupport() { public FunctionRenderingSupport getRenderingSupport() {
return renderingSupport; return renderingSupport;
} }
private static List<SqlAstNode> resolveSqlAstArguments(List<SqmTypedNode<?>> sqmArguments, SqmToSqlAstConverter walker) { protected static List<SqlAstNode> resolveSqlAstArguments(List<SqmTypedNode<?>> sqmArguments, SqmToSqlAstConverter walker) {
if ( sqmArguments == null || sqmArguments.isEmpty() ) { if ( sqmArguments == null || sqmArguments.isEmpty() ) {
return emptyList(); return emptyList();
} }
final ArrayList<SqlAstNode> sqlAstArguments = new ArrayList<>(); final ArrayList<SqlAstNode> sqlAstArguments = new ArrayList<>( sqmArguments.size() );
for ( SqmTypedNode<?> sqmArgument : sqmArguments ) { for ( SqmTypedNode<?> sqmArgument : sqmArguments ) {
sqlAstArguments.add((SqlAstNode) ((SqmVisitableNode) sqmArgument).accept(walker)); sqlAstArguments.add( (SqlAstNode) ( (SqmVisitableNode) sqmArgument ).accept( walker ) );
} }
return sqlAstArguments; return sqlAstArguments;
} }
@Override @Override
public SelfRenderingFunctionSqlAstExpression convertToSqlAst(SqmToSqlAstConverter walker) { public SelfRenderingFunctionSqlAstExpression convertToSqlAst(SqmToSqlAstConverter walker) {
resolveResultType( walker.getCreationContext().getDomainModel().getTypeConfiguration() ); final AllowableFunctionReturnType<?> resultType = resolveResultType(
walker.getCreationContext().getDomainModel().getTypeConfiguration()
);
return new SelfRenderingFunctionSqlAstExpression( return new SelfRenderingFunctionSqlAstExpression(
getFunctionName(),
getRenderingSupport(), getRenderingSupport(),
resolveSqlAstArguments( getArguments(), walker), resolveSqlAstArguments( getArguments(), walker ),
resultType, resultType,
getMappingModelExpressable( walker, resultType ) getMappingModelExpressable( walker, resultType )
); );
@ -93,7 +88,7 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
return nodeType; return nodeType;
} }
private void resolveResultType(TypeConfiguration typeConfiguration) { protected AllowableFunctionReturnType<?> resolveResultType(TypeConfiguration typeConfiguration) {
if ( resultType == null ) { if ( resultType == null ) {
resultType = returnTypeResolver.resolveFunctionReturnType( resultType = returnTypeResolver.resolveFunctionReturnType(
impliedResultType, impliedResultType,
@ -102,9 +97,10 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
); );
setExpressableType( resultType ); setExpressableType( resultType );
} }
return resultType;
} }
private MappingModelExpressable<?> getMappingModelExpressable( protected MappingModelExpressable<?> getMappingModelExpressable(
SqmToSqlAstConverter walker, SqmToSqlAstConverter walker,
AllowableFunctionReturnType<?> resultType) { AllowableFunctionReturnType<?> resultType) {
MappingModelExpressable<?> mapping; MappingModelExpressable<?> mapping;
@ -131,17 +127,12 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
return null; // this works at least approximately return null; // this works at least approximately
} }
}, },
resolveSqlAstArguments( arguments, walker ) resolveSqlAstArguments( getArguments(), walker )
); );
} }
return mapping; return mapping;
} }
@Override
public String getFunctionName() {
return name;
}
@Override @Override
public void applySqlSelections(DomainResultCreationState creationState) { public void applySqlSelections(DomainResultCreationState creationState) {
//implemented on SelfRenderingFunctionSqlAstExpression //implemented on SelfRenderingFunctionSqlAstExpression

View File

@ -293,13 +293,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
private static final Logger log = Logger.getLogger( BaseSqmToSqlAstConverter.class ); private static final Logger log = Logger.getLogger( BaseSqmToSqlAstConverter.class );
protected enum Shallowness {
NONE,
CTOR,
FUNCTION,
SUBQUERY
}
private final SqlAstCreationContext creationContext; private final SqlAstCreationContext creationContext;
private final SqmStatement<?> statement; private final SqmStatement<?> statement;
@ -326,7 +319,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
private FromClauseIndex lastPoppedFromClauseIndex; private FromClauseIndex lastPoppedFromClauseIndex;
private final Stack<Clause> currentClauseStack = new StandardStack<>(); private final Stack<Clause> currentClauseStack = new StandardStack<>();
private final Stack<Shallowness> shallownessStack = new StandardStack<>( Shallowness.NONE );
private SqmByUnit appliedByUnit; private SqmByUnit appliedByUnit;
private Expression adjustedTimestamp; private Expression adjustedTimestamp;
@ -1331,7 +1323,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public SelectClause visitSelectClause(SqmSelectClause selectClause) { public SelectClause visitSelectClause(SqmSelectClause selectClause) {
currentClauseStack.push( Clause.SELECT ); currentClauseStack.push( Clause.SELECT );
shallownessStack.push( Shallowness.SUBQUERY );
try { try {
super.visitSelectClause( selectClause ); super.visitSelectClause( selectClause );
@ -1340,7 +1331,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return sqlSelectClause; return sqlSelectClause;
} }
finally { finally {
shallownessStack.pop();
currentClauseStack.pop(); currentClauseStack.pop();
} }
} }
@ -2297,12 +2287,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Expression visitFunction(SqmFunction sqmFunction) { public Expression visitFunction(SqmFunction sqmFunction) {
inferableTypeAccessStack.push( () -> null ); inferableTypeAccessStack.push( () -> null );
shallownessStack.push( Shallowness.FUNCTION );
try { try {
return sqmFunction.convertToSqlAst( this ); return sqmFunction.convertToSqlAst( this );
} }
finally { finally {
shallownessStack.pop();
inferableTypeAccessStack.pop(); inferableTypeAccessStack.pop();
} }
} }
@ -2319,61 +2307,37 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Object visitTrimSpecification(SqmTrimSpecification specification) { public Object visitTrimSpecification(SqmTrimSpecification specification) {
shallownessStack.push( Shallowness.FUNCTION ); return new TrimSpecification( specification.getSpecification() );
try {
return new TrimSpecification( specification.getSpecification() );
}
finally {
shallownessStack.pop();
}
} }
@Override @Override
public Object visitCastTarget(SqmCastTarget target) { public Object visitCastTarget(SqmCastTarget target) {
shallownessStack.push( Shallowness.FUNCTION ); BasicValuedMapping targetType = (BasicValuedMapping) target.getType();
try { if ( targetType instanceof BasicType<?> ) {
BasicValuedMapping targetType = (BasicValuedMapping) target.getType(); targetType = InferredBasicValueResolver.resolveSqlTypeIndicators( this, (BasicType<?>) targetType );
if ( targetType instanceof BasicType<?> ) {
targetType = InferredBasicValueResolver.resolveSqlTypeIndicators( this, (BasicType<?>) targetType );
}
return new CastTarget(
targetType,
target.getLength(),
target.getPrecision(),
target.getScale()
);
}
finally {
shallownessStack.pop();
} }
return new CastTarget(
targetType,
target.getLength(),
target.getPrecision(),
target.getScale()
);
} }
@Override @Override
public Object visitExtractUnit(SqmExtractUnit unit) { public Object visitExtractUnit(SqmExtractUnit unit) {
shallownessStack.push( Shallowness.FUNCTION ); return new ExtractUnit(
try { unit.getUnit(),
return new ExtractUnit( (BasicValuedMapping) unit.getType()
unit.getUnit(), );
(BasicValuedMapping) unit.getType()
);
}
finally {
shallownessStack.pop();
}
} }
@Override @Override
public Object visitDurationUnit(SqmDurationUnit unit) { public Object visitDurationUnit(SqmDurationUnit unit) {
shallownessStack.push( Shallowness.FUNCTION ); return new DurationUnit(
try { unit.getUnit(),
return new DurationUnit( (BasicValuedMapping) unit.getType()
unit.getUnit(), );
(BasicValuedMapping) unit.getType()
);
}
finally {
shallownessStack.pop();
}
} }
@Override @Override
@ -2764,18 +2728,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Object visitUnaryOperationExpression(SqmUnaryOperation expression) { public Object visitUnaryOperationExpression(SqmUnaryOperation expression) {
shallownessStack.push( Shallowness.NONE ); return new UnaryOperation(
interpret( expression.getOperation() ),
try { toSqlExpression( expression.getOperand().accept( this ) ),
return new UnaryOperation( (BasicValuedMapping) determineValueMapping( expression.getOperand() )
interpret( expression.getOperation() ), );
toSqlExpression( expression.getOperand().accept( this ) ),
(BasicValuedMapping) determineValueMapping( expression.getOperand() )
);
}
finally {
shallownessStack.pop();
}
} }
private UnaryArithmeticOperator interpret(UnaryArithmeticOperator operator) { private UnaryArithmeticOperator interpret(UnaryArithmeticOperator operator) {
@ -2784,52 +2741,45 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Object visitBinaryArithmeticExpression(SqmBinaryArithmetic expression) { public Object visitBinaryArithmeticExpression(SqmBinaryArithmetic expression) {
shallownessStack.push( Shallowness.NONE ); SqmExpression leftOperand = expression.getLeftHandOperand();
SqmExpression rightOperand = expression.getRightHandOperand();
try { boolean durationToRight = TypeConfiguration.isDuration( rightOperand.getNodeType() );
SqmExpression leftOperand = expression.getLeftHandOperand(); TypeConfiguration typeConfiguration = getCreationContext().getDomainModel().getTypeConfiguration();
SqmExpression rightOperand = expression.getRightHandOperand(); TemporalType temporalTypeToLeft = typeConfiguration.getSqlTemporalType( leftOperand.getNodeType() );
TemporalType temporalTypeToRight = typeConfiguration.getSqlTemporalType( rightOperand.getNodeType() );
boolean temporalTypeSomewhereToLeft = adjustedTimestamp != null || temporalTypeToLeft != null;
boolean durationToRight = TypeConfiguration.isDuration( rightOperand.getNodeType() ); if ( temporalTypeToLeft != null && durationToRight ) {
TypeConfiguration typeConfiguration = getCreationContext().getDomainModel().getTypeConfiguration(); if ( adjustmentScale != null || negativeAdjustment ) {
TemporalType temporalTypeToLeft = typeConfiguration.getSqlTemporalType( leftOperand.getNodeType() ); //we can't distribute a scale over a date/timestamp
TemporalType temporalTypeToRight = typeConfiguration.getSqlTemporalType( rightOperand.getNodeType() ); throw new SemanticException( "scalar multiplication of temporal value" );
boolean temporalTypeSomewhereToLeft = adjustedTimestamp != null || temporalTypeToLeft != null;
if ( temporalTypeToLeft != null && durationToRight ) {
if ( adjustmentScale != null || negativeAdjustment ) {
//we can't distribute a scale over a date/timestamp
throw new SemanticException( "scalar multiplication of temporal value" );
}
}
if ( durationToRight && temporalTypeSomewhereToLeft ) {
return transformDurationArithmetic( expression );
}
else if ( temporalTypeToLeft != null && temporalTypeToRight != null ) {
return transformDatetimeArithmetic( expression );
}
else if ( durationToRight && appliedByUnit != null ) {
return new BinaryArithmeticExpression(
toSqlExpression( leftOperand.accept( this ) ),
expression.getOperator(),
toSqlExpression( rightOperand.accept( this ) ),
//after distributing the 'by unit' operator
//we always get a Long value back
(BasicValuedMapping) appliedByUnit.getNodeType()
);
}
else {
return new BinaryArithmeticExpression(
toSqlExpression( leftOperand.accept( this ) ),
expression.getOperator(),
toSqlExpression( rightOperand.accept( this ) ),
getExpressionType( expression )
);
} }
} }
finally {
shallownessStack.pop(); if ( durationToRight && temporalTypeSomewhereToLeft ) {
return transformDurationArithmetic( expression );
}
else if ( temporalTypeToLeft != null && temporalTypeToRight != null ) {
return transformDatetimeArithmetic( expression );
}
else if ( durationToRight && appliedByUnit != null ) {
return new BinaryArithmeticExpression(
toSqlExpression( leftOperand.accept( this ) ),
expression.getOperator(),
toSqlExpression( rightOperand.accept( this ) ),
//after distributing the 'by unit' operator
//we always get a Long value back
(BasicValuedMapping) appliedByUnit.getNodeType()
);
}
else {
return new BinaryArithmeticExpression(
toSqlExpression( leftOperand.accept( this ) ),
expression.getOperator(),
toSqlExpression( rightOperand.accept( this ) ),
getExpressionType( expression )
);
} }
} }

View File

@ -0,0 +1,23 @@
/*
* 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.sql.ast.tree.expression;
import java.util.List;
import org.hibernate.sql.ast.tree.SqlAstNode;
/**
* Models a function expression at the SQL AST level.
*
* @author Christian Beikov
*/
public interface FunctionExpression extends Expression {
String getFunctionName();
List<SqlAstNode> getArguments();
}