diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index 18336a539d12..610d63fe0b56 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -29,6 +29,7 @@ import org.hibernate.dialect.Replacer; import org.hibernate.dialect.SelectItemReferenceStrategy; import org.hibernate.dialect.function.InsertSubstringOverlayEmulation; +import org.hibernate.dialect.function.TrimFunction; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; @@ -38,6 +39,7 @@ import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; import org.hibernate.query.sqm.function.SqmFunctionRegistry; +import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.jdbc.VarcharUUIDJdbcType; import org.hibernate.dialect.function.CaseLeastGreatestEmulation; @@ -391,6 +393,18 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio functionRegistry.register( "overlay", new InsertSubstringOverlayEmulation( typeConfiguration, true ) ); + + // coalesce() has a bug where it does not accept parameters + // as arguments, even with a cast (on Informix 14) + functionRegistry.namedDescriptorBuilder( "coalesce" ) + .setMinArgumentCount( 1 ) + .setArgumentRenderingMode( SqlAstNodeRenderingMode.INLINE_PARAMETERS ) + .setArgumentTypeResolver( StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE ) + .register(); + + // parameter arguments to trim() require a cast + functionContributions.getFunctionRegistry().register( "trim", + new TrimFunction( this, typeConfiguration, SqlAstNodeRenderingMode.NO_UNTYPED ) ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java index d5c3b92d9701..824b4d9ef648 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java @@ -20,7 +20,6 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.FunctionExpression; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.from.NamedTableReference; @@ -191,18 +190,6 @@ else if ( expression instanceof Summarization ) { } } -// @Override -// protected void renderNull(Literal literal) { -// if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.NO_UNTYPED ) { -// renderCasted( literal ); -// } -// else { -// int sqlType = literal.getExpressionType().getSingleJdbcMapping().getJdbcType().getJdbcTypeCode(); -// String nullString = getDialect().getSelectClauseNullString( sqlType, getSessionFactory().getTypeConfiguration() ); -// appendSql( nullString ); -// } -// } - @Override protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) { renderIntoIntoAndTable( tableInsert ); @@ -295,36 +282,22 @@ protected void visitArithmeticOperand(Expression expression) { } } - @Override - public void visitSelfRenderingExpression(SelfRenderingExpression expression) { - final boolean isStringFunctionWithParameterArg = - expression instanceof FunctionExpression fn - && expression.getExpressionType() != null - && expression.getExpressionType().getJdbcTypeCount() == 1 - && expression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString() - && fn.getArguments().stream().anyMatch( arg -> arg instanceof SqmParameterInterpretation ); - if ( isStringFunctionWithParameterArg ) { - append( "cast(" ); - } - super.visitSelfRenderingExpression( expression ); - if ( isStringFunctionWithParameterArg ) { - append( " as lvarchar)" ); - } + private static boolean isConcatFunction(Expression expression) { + return expression instanceof FunctionExpression fn + && fn.getFunctionName().equals( "concat" ); } private void caseArgument(Expression expression) { - // concatenation inside a case must be cast to varchar(255) - // or we get a bunch of trailing whitespace - final boolean concat = - expression instanceof FunctionExpression fn - && fn.getFunctionName().equals( "concat" ); - if ( concat ) { + if ( isConcatFunction( expression ) ) { + // concatenation inside a case must be cast to varchar(255) + // or we get a bunch of trailing whitespace append( "cast(" ); - } - expression.accept( this ); - if ( concat ) { + expression.accept( this ); append( " as varchar(255))"); } + else { + expression.accept( this ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CastFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CastFunction.java index a0471b4fa516..cb0dde411195 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CastFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CastFunction.java @@ -77,9 +77,6 @@ public void render( renderCastArrayToString( sqlAppender, arguments.get( 0 ), dialect, walker ); } else { - if ( source instanceof QueryLiteral literal && literal.getLiteralValue() == null ) { - literal.setInCast(true); - } new PatternRenderer( dialect.castPattern( sourceType, targetType ) ) .render( sqlAppender, arguments, walker ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java index f647635d372d..4ab85b5e352e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java @@ -19,10 +19,11 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.SortSpecification; -import java.util.Collections; import java.util.List; import java.util.Locale; +import static java.util.Collections.emptyList; + /** * Provides a standard implementation that supports the majority of the HQL * functions that are translated to SQL. The Dialect and its sub-classes use @@ -119,7 +120,7 @@ public void render( List sqlAstArguments, ReturnableType returnType, SqlAstTranslator translator) { - render( sqlAppender, sqlAstArguments, null, Collections.emptyList(), null, null, translator ); + render( sqlAppender, sqlAstArguments, null, emptyList(), null, null, translator ); } @Override @@ -129,7 +130,7 @@ public void render( Predicate filter, ReturnableType returnType, SqlAstTranslator translator) { - render( sqlAppender, sqlAstArguments, filter, Collections.emptyList(), null, null, translator ); + render( sqlAppender, sqlAstArguments, filter, emptyList(), null, null, translator ); } @Override @@ -152,7 +153,7 @@ public void render( Boolean fromFirst, ReturnableType returnType, SqlAstTranslator walker) { - render( sqlAppender, sqlAstArguments, filter, Collections.emptyList(), respectNulls, fromFirst, walker ); + render( sqlAppender, sqlAstArguments, filter, emptyList(), respectNulls, fromFirst, walker ); } private void render( @@ -164,7 +165,10 @@ private void render( Boolean fromFirst, SqlAstTranslator translator) { final boolean useParens = useParenthesesWhenNoArgs || !sqlAstArguments.isEmpty(); - final boolean caseWrapper = filter != null && !translator.getSessionFactory().getJdbcServices().getDialect(). supportsFilterClause(); + final boolean caseWrapper = + filter != null + && !translator.getSessionFactory().getJdbcServices().getDialect() + .supportsFilterClause(); sqlAppender.appendSql( functionName ); if ( useParens ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 9f755003fa83..601d38bf336e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -7057,23 +7057,20 @@ public void visitOverflow(Overflow overflow) { public void visitParameter(JdbcParameter jdbcParameter) { switch ( getParameterRenderingMode() ) { case NO_UNTYPED: - case NO_PLAIN_PARAMETER: { + case NO_PLAIN_PARAMETER: renderCasted( jdbcParameter ); break; - } case INLINE_PARAMETERS: - case INLINE_ALL_PARAMETERS: { + case INLINE_ALL_PARAMETERS: renderExpressionAsLiteral( jdbcParameter, jdbcParameterBindings ); break; - } case WRAP_ALL_PARAMETERS: renderWrappedParameter( jdbcParameter ); break; case DEFAULT: - default: { + default: visitParameterAsParameter( jdbcParameter ); break; - } } } @@ -7115,8 +7112,9 @@ protected void renderParameterAsParameter(int position, JdbcParameter jdbcParame @Override public void render(SqlAstNode sqlAstNode, SqlAstNodeRenderingMode renderingMode) { - SqlAstNodeRenderingMode original = this.parameterRenderingMode; - if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { + final SqlAstNodeRenderingMode original = this.parameterRenderingMode; + if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS + && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { this.parameterRenderingMode = renderingMode; } try { @@ -7128,8 +7126,9 @@ public void render(SqlAstNode sqlAstNode, SqlAstNodeRenderingMode renderingMode) } protected void withParameterRenderingMode(SqlAstNodeRenderingMode renderingMode, Runnable runnable) { - SqlAstNodeRenderingMode original = this.parameterRenderingMode; - if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { + final SqlAstNodeRenderingMode original = this.parameterRenderingMode; + if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS + && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { this.parameterRenderingMode = renderingMode; } try { @@ -7351,7 +7350,7 @@ protected void visitAnsiCaseSearchedExpression( Consumer resultRenderer) { appendSql( "case" ); final SqlAstNodeRenderingMode original = this.parameterRenderingMode; - for ( CaseSearchedExpression.WhenFragment whenFragment : caseSearchedExpression.getWhenFragments() ) { + for ( var whenFragment : caseSearchedExpression.getWhenFragments() ) { if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { this.parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; @@ -7375,12 +7374,12 @@ protected void visitAnsiCaseSearchedExpression( protected void visitDecodeCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression) { appendSql( "decode( " ); final SqlAstNodeRenderingMode original = this.parameterRenderingMode; - final List whenFragments = caseSearchedExpression.getWhenFragments(); + final var whenFragments = caseSearchedExpression.getWhenFragments(); final int caseNumber = whenFragments.size(); CaseSearchedExpression.WhenFragment firstWhenFragment = null; for ( int i = 0; i < caseNumber; i++ ) { - final CaseSearchedExpression.WhenFragment whenFragment = whenFragments.get( i ); - Predicate predicate = whenFragment.getPredicate(); + final var whenFragment = whenFragments.get( i ); + final Predicate predicate = whenFragment.getPredicate(); if ( original != SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS && original != SqlAstNodeRenderingMode.WRAP_ALL_PARAMETERS ) { this.parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; @@ -7504,12 +7503,7 @@ public void visitJdbcLiteral(JdbcLiteral jdbcLiteral) { @Override public void visitQueryLiteral(QueryLiteral queryLiteral) { - if ( queryLiteral.getLiteralValue() == null && queryLiteral.isInCast() ) { - appendSql( SqlAppender.NULL_KEYWORD ); - } - else { - visitLiteral( queryLiteral ); - } + visitLiteral( queryLiteral ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java index b8958b373dc5..9cef9ff1feda 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/QueryLiteral.java @@ -30,7 +30,6 @@ public class QueryLiteral implements Literal, DomainResultProducer { private final T value; private final SqlExpressible expressible; - private boolean inCast; public QueryLiteral(T value, SqlExpressible expressible) { assert value == null || expressible.getJdbcMapping().getJdbcJavaType().isInstance( value ); @@ -101,12 +100,4 @@ public void applySqlSelections(DomainResultCreationState creationState) { creationState.getSqlAstCreationState().getCreationContext().getMappingMetamodel().getTypeConfiguration() ); } - - public void setInCast(boolean inCast) { - this.inCast = inCast; - } - - public boolean isInCast() { - return inCast; - } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java index 698ebdb9ce9b..570054d76b58 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterParameterTests.java @@ -19,22 +19,14 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.community.dialect.AltibaseDialect; -import org.hibernate.community.dialect.FirebirdDialect; -import org.hibernate.community.dialect.InformixDialect; -import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.CockroachDialect; -import org.hibernate.dialect.DB2Dialect; -import org.hibernate.community.dialect.DerbyDialect; -import org.hibernate.dialect.H2Dialect; -import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; -import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgresPlusDialect; import org.hibernate.dialect.SQLServerDialect; -import org.hibernate.dialect.SybaseDialect; import org.hibernate.community.dialect.TiDBDialect; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.type.NumericBooleanConverter; import org.hibernate.type.YesNoConverter; @@ -92,21 +84,9 @@ public void testYesNo(BiConsumer> inTransaction) { scope.inTransaction( (session) -> { session.disableFilter( "subDepartmentFilter" ); @@ -147,20 +127,9 @@ public void testNumeric(BiConsumer> inTransaction) { scope.inTransaction( (session) -> { session.disableFilter( "subDepartmentFilter" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index e51981952ba8..53a6499b40e0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -915,6 +915,9 @@ public void testPadFunction(SessionFactoryScope scope) { is(".....hello")); assertThat(session.createQuery("select pad('hello' with 10 trailing '.')", String.class).getSingleResult(), is("hello.....")); + + assertThat(session.createQuery("select pad(:hello with 10 leading)", String.class).setParameter( "hello", "hello" ).getSingleResult(), + is(" hello")); } ); @@ -2020,6 +2023,10 @@ public void testIntervalDiffExpressions(SessionFactoryScope scope) { session.createQuery("select (e.theDate - e.theDate) by day from EntityOfBasics e", Long.class) .list(); + //TODO +// session.createQuery("select (e.theDate - e.theDate) by week from EntityOfBasics e", Long.class) +// .list(); + session.createQuery("select (e.theTimestamp - e.theTimestamp) by hour from EntityOfBasics e", Long.class) .list(); session.createQuery("select (e.theTimestamp - e.theTimestamp) by minute from EntityOfBasics e", Long.class)