Properly handle parameters and null literals as plain select items

This commit is contained in:
Christian Beikov 2021-02-25 10:47:14 +01:00
parent 5b5254fbd6
commit 37b03ecc05
3 changed files with 72 additions and 35 deletions

View File

@ -19,10 +19,10 @@ import org.hibernate.query.sqm.tree.SqmVisitableNode;
* @author Gavin King * @author Gavin King
*/ */
public class SqmCastTarget<T> extends AbstractSqmNode implements SqmTypedNode<T>, SqmVisitableNode { public class SqmCastTarget<T> extends AbstractSqmNode implements SqmTypedNode<T>, SqmVisitableNode {
private AllowableFunctionReturnType<T> type; private final AllowableFunctionReturnType<T> type;
private Long length; private final Long length;
private Integer precision; private final Integer precision;
private Integer scale; private final Integer scale;
public Long getLength() { public Long getLength() {
return length; return length;

View File

@ -32,6 +32,7 @@ import org.hibernate.internal.FilterJdbcParameter;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
@ -49,6 +50,7 @@ import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.tree.cte.CteColumn;
import org.hibernate.sql.ast.tree.cte.CteContainer; import org.hibernate.sql.ast.tree.cte.CteContainer;
import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteStatement;
@ -1807,7 +1809,7 @@ public abstract class AbstractSqlAstWalker implements SqlAstWalker, SqlAppender
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i ); final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator ); appendSql( separator );
sqlSelection.accept( this ); visitSqlSelection( sqlSelection );
appendSql( " c" ); appendSql( " c" );
appendSql( Integer.toString( i ) ); appendSql( Integer.toString( i ) );
separator = COMA_SEPARATOR; separator = COMA_SEPARATOR;
@ -1851,7 +1853,7 @@ public abstract class AbstractSqlAstWalker implements SqlAstWalker, SqlAppender
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final SqlSelection sqlSelection = sqlSelections.get( i ); final SqlSelection sqlSelection = sqlSelections.get( i );
appendSql( separator ); appendSql( separator );
sqlSelection.accept( this ); visitSqlSelection( sqlSelection );
separator = COMA_SEPARATOR; separator = COMA_SEPARATOR;
} }
} }
@ -1934,9 +1936,63 @@ public abstract class AbstractSqlAstWalker implements SqlAstWalker, SqlAppender
@Override @Override
public void visitSqlSelection(SqlSelection sqlSelection) { public void visitSqlSelection(SqlSelection sqlSelection) {
// do nothing... this is handled #visitSelectClause final Expression expression = sqlSelection.getExpression();
// Null literals have to be casted in the select clause
if ( expression instanceof Literal ) {
final Literal literal = (Literal) expression;
if ( literal.getLiteralValue() == null ) {
renderNullCast( literal );
}
else {
renderLiteral( literal, dialect.requiresCastingOfParametersInSelectClause() );
}
}
else if ( expression instanceof NullnessLiteral ) {
renderNullCast( expression );
}
else {
expression.accept( this );
}
} }
protected void renderNullCast(Expression expression) {
final List<SqlAstNode> arguments = new ArrayList<>( 2 );
arguments.add( expression );
arguments.add( new CastTarget( (BasicValuedMapping) expression.getExpressionType() ) );
castFunction().render( this, arguments, this );
}
@SuppressWarnings("unchecked")
protected void renderLiteral(Literal literal, boolean castParameter) {
assert literal.getExpressionType().getJdbcTypeCount() == 1;
final JdbcMapping jdbcMapping = literal.getJdbcMapping();
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getSqlTypeDescriptor()
.getJdbcLiteralFormatter( jdbcMapping.getJavaTypeDescriptor() );
// If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter
if ( literalFormatter == null ) {
parameterBinders.add( literal );
final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal );
if ( castParameter ) {
final List<SqlAstNode> arguments = new ArrayList<>( 2 );
arguments.add( jdbcParameter );
arguments.add( new CastTarget( (BasicValuedMapping) jdbcMapping ) );
castFunction().render( this, arguments, this );
}
else {
appendSql( PARAM_MARKER );
}
}
else {
appendSql(
literalFormatter.toJdbcLiteral(
literal.getLiteralValue(),
dialect,
getWrapperOptions()
)
);
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// FROM clause // FROM clause
@ -2617,36 +2673,13 @@ public abstract class AbstractSqlAstWalker implements SqlAstWalker, SqlAppender
appendSql( "null" ); appendSql( "null" );
} }
@SuppressWarnings("unchecked")
private void visitLiteral(Literal literal) { private void visitLiteral(Literal literal) {
if ( literal.getLiteralValue() == null ) { if ( literal.getLiteralValue() == null ) {
// todo : not sure we allow this "higher up" // todo : not sure we allow this "higher up"
appendSql( SqlAppender.NULL_KEYWORD ); appendSql( SqlAppender.NULL_KEYWORD );
} }
else { else {
assert literal.getExpressionType().getJdbcTypeCount() == 1; renderLiteral( literal, false );
final JdbcMapping jdbcMapping = literal.getJdbcMapping();
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getSqlTypeDescriptor().getJdbcLiteralFormatter( jdbcMapping.getJavaTypeDescriptor() );
if ( literalFormatter == null ) {
parameterBinders.add( literal );
final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal );
if ( clauseStack.getCurrent() == Clause.SELECT && dialect.requiresCastingOfParametersInSelectClause() ) {
castFunction().render( this, Collections.singletonList( jdbcParameter ), this );
}
else {
appendSql( PARAM_MARKER );
}
}
else {
appendSql(
literalFormatter.toJdbcLiteral(
literal.getLiteralValue(),
dialect,
getWrapperOptions()
)
);
}
} }
} }

View File

@ -14,10 +14,14 @@ import org.hibernate.sql.ast.tree.SqlAstNode;
* @author Gavin King * @author Gavin King
*/ */
public class CastTarget implements Expression, SqlAstNode { public class CastTarget implements Expression, SqlAstNode {
private BasicValuedMapping type; private final BasicValuedMapping type;
private Long length; private final Long length;
private Integer precision; private final Integer precision;
private Integer scale; private final Integer scale;
public CastTarget(BasicValuedMapping type) {
this( type, null, null, null );
}
public CastTarget(BasicValuedMapping type, Long length, Integer precision, Integer scale) { public CastTarget(BasicValuedMapping type, Long length, Integer precision, Integer scale) {
this.type = type; this.type = type;