HHH-17355 Smoothen some rough edges with parameter typing and PG12 support

This commit is contained in:
Christian Beikov 2023-11-06 16:03:11 +01:00
parent d7bdb5c009
commit c700dcd8b6
41 changed files with 374 additions and 101 deletions

View File

@ -477,8 +477,8 @@ public class CockroachLegacyDialect extends Dialect {
functionFactory.arrayRemoveIndex_unnest( true );
functionFactory.arraySlice_operator();
functionFactory.arrayReplace();
functionFactory.arrayTrim_trim_array();
functionFactory.arrayFill_postgresql();
functionFactory.arrayTrim_unnest();
functionFactory.arrayFill_cockroachdb();
functionFactory.arrayToString_postgresql();
functionContributions.getFunctionRegistry().register(

View File

@ -597,7 +597,12 @@ public class PostgreSQLLegacyDialect extends Dialect {
functionFactory.arrayRemoveIndex_unnest( true );
functionFactory.arraySlice_operator();
functionFactory.arrayReplace();
functionFactory.arrayTrim_trim_array();
if ( getVersion().isSameOrAfter( 14 ) ) {
functionFactory.arrayTrim_trim_array();
}
else {
functionFactory.arrayTrim_unnest();
}
functionFactory.arrayFill_postgresql();
functionFactory.arrayToString_postgresql();

View File

@ -464,8 +464,8 @@ public class CockroachDialect extends Dialect {
functionFactory.arrayRemoveIndex_unnest( true );
functionFactory.arraySlice_operator();
functionFactory.arrayReplace();
functionFactory.arrayTrim_trim_array();
functionFactory.arrayFill_postgresql();
functionFactory.arrayTrim_unnest();
functionFactory.arrayFill_cockroachdb();
functionFactory.arrayToString_postgresql();
functionContributions.getFunctionRegistry().register(

View File

@ -46,7 +46,7 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
}
public static String getSqlType(CastTarget castTarget, SessionFactoryImplementor factory) {
final String sqlType = getCastTypeName( castTarget, factory );
final String sqlType = getCastTypeName( castTarget, factory.getTypeConfiguration() );
return getSqlType( castTarget, sqlType, factory.getJdbcServices().getDialect() );
}

View File

@ -645,7 +645,12 @@ public class PostgreSQLDialect extends Dialect {
functionFactory.arrayRemoveIndex_unnest( true );
functionFactory.arraySlice_operator();
functionFactory.arrayReplace();
functionFactory.arrayTrim_trim_array();
if ( getVersion().isSameOrAfter( 14 ) ) {
functionFactory.arrayTrim_trim_array();
}
else {
functionFactory.arrayTrim_unnest();
}
functionFactory.arrayFill_postgresql();
functionFactory.arrayToString_postgresql();

View File

@ -30,6 +30,7 @@ import org.hibernate.dialect.function.array.ArraySetUnnestFunction;
import org.hibernate.dialect.function.array.ArraySliceUnnestFunction;
import org.hibernate.dialect.function.array.ArrayToStringFunction;
import org.hibernate.dialect.function.array.ArrayViaArgumentReturnTypeResolver;
import org.hibernate.dialect.function.array.CockroachArrayFillFunction;
import org.hibernate.dialect.function.array.ElementViaArrayArgumentReturnTypeResolver;
import org.hibernate.dialect.function.array.H2ArrayContainsFunction;
import org.hibernate.dialect.function.array.H2ArrayFillFunction;
@ -72,6 +73,7 @@ import org.hibernate.dialect.function.array.OracleArrayAggEmulation;
import org.hibernate.dialect.function.array.OracleArrayConstructorFunction;
import org.hibernate.dialect.function.array.OracleArrayContainsFunction;
import org.hibernate.dialect.function.array.PostgreSQLArrayPositionsFunction;
import org.hibernate.dialect.function.array.PostgreSQLArrayTrimEmulation;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
@ -3166,6 +3168,13 @@ public class CommonFunctionFactory {
.register();
}
/**
* PostgreSQL array_trim() emulation for versions before 14
*/
public void arrayTrim_unnest() {
functionRegistry.register( "array_trim", new PostgreSQLArrayTrimEmulation() );
}
/**
* Oracle array_trim() function
*/
@ -3197,6 +3206,14 @@ public class CommonFunctionFactory {
functionRegistry.register( "array_fill_list", new PostgreSQLArrayFillFunction( true ) );
}
/**
* Cockroach array_fill() function
*/
public void arrayFill_cockroachdb() {
functionRegistry.register( "array_fill", new CockroachArrayFillFunction( false ) );
functionRegistry.register( "array_fill_list", new CockroachArrayFillFunction( true ) );
}
/**
* Oracle array_fill() function
*/

View File

@ -0,0 +1,37 @@
/*
* 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.dialect.function.array;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
/**
* Encapsulates the validator, return type and argument type resolvers for the array_remove functions.
* Subclasses only have to implement the rendering.
*/
public abstract class AbstractArrayTrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public AbstractArrayTrimFunction() {
super(
"array_trim",
StandardArgumentsValidators.composite(
new ArgumentTypesValidator( null, ANY, INTEGER ),
ArrayArgumentValidator.DEFAULT_INSTANCE
),
ArrayViaArgumentReturnTypeResolver.DEFAULT_INSTANCE,
StandardFunctionArgumentTypeResolvers.composite(
StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER ),
StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE
)
);
}
}

View File

@ -65,7 +65,7 @@ public class ArrayContainsOperatorFunction extends ArrayContainsUnnestFunction {
sqlAppender.append( "] as " );
sqlAppender.append( DdlTypeHelper.getCastTypeName(
haystackExpression.getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
) );
sqlAppender.append( ')' );
}

View File

@ -61,7 +61,10 @@ public class ArrayRemoveIndexUnnestFunction extends AbstractSqmSelfRenderingFunc
sqlAppender.append( "),");
if ( castEmptyArrayLiteral ) {
sqlAppender.append( "cast(array[] as " );
sqlAppender.append( DdlTypeHelper.getCastTypeName( returnType, walker ) );
sqlAppender.append( DdlTypeHelper.getCastTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
) );
sqlAppender.append( ')' );
}
else {

View File

@ -68,7 +68,10 @@ public class ArraySliceUnnestFunction extends AbstractSqmSelfRenderingFunctionDe
sqlAppender.append( "),");
if ( castEmptyArrayLiteral ) {
sqlAppender.append( "cast(array[] as " );
sqlAppender.append( DdlTypeHelper.getCastTypeName( returnType, walker ) );
sqlAppender.append( DdlTypeHelper.getCastTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
) );
sqlAppender.append( ')' );
}
else {

View File

@ -0,0 +1,66 @@
/*
* 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.dialect.function.array;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
/**
* Implement the array fill function by using {@code generate_series}.
*/
public class CockroachArrayFillFunction extends AbstractArrayFillFunction {
public CockroachArrayFillFunction(boolean list) {
super( list );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
sqlAppender.append( "coalesce(case when " );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( "<>0 then (select array_agg(" );
final String elementCastType;
final Expression elementExpression = (Expression) sqlAstArguments.get( 0 );
if ( needsElementCasting( elementExpression ) ) {
elementCastType = DdlTypeHelper.getCastTypeName(
elementExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( "cast(" );
}
else {
elementCastType = null;
}
sqlAstArguments.get( 0 ).accept( walker );
if ( elementCastType != null ) {
sqlAppender.append( " as " );
sqlAppender.append( elementCastType );
sqlAppender.append( ')' );
}
sqlAppender.append( ") from generate_series(1," );
sqlAstArguments.get( 1 ).accept( walker );
sqlAppender.append( ",1))) end,array[])" );
}
private static boolean needsElementCasting(Expression elementExpression) {
// PostgreSQL needs casting of null and string literal expressions
return elementExpression instanceof Literal && (
elementExpression.getExpressionType().getSingleJdbcMapping().getJdbcType().isString()
|| ( (Literal) elementExpression ).getLiteralValue() == null
);
}
}

View File

@ -16,7 +16,6 @@ import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.SqlTypedMapping;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
@ -60,71 +59,91 @@ public class DdlTypeHelper {
);
}
public static String getTypeName(BasicType<?> type, SqlAstTranslator<?> walker) {
return getTypeName( (JdbcMappingContainer) type, walker );
public static String getTypeName(BasicType<?> type, TypeConfiguration typeConfiguration) {
return getTypeName( (JdbcMappingContainer) type, typeConfiguration );
}
public static String getTypeName(JdbcMappingContainer type, SqlAstTranslator<?> walker) {
public static String getTypeName(BasicType<?> type, Size size, TypeConfiguration typeConfiguration) {
return getTypeName( (JdbcMappingContainer) type, size, typeConfiguration );
}
public static String getTypeName(JdbcMappingContainer type, TypeConfiguration typeConfiguration) {
return getTypeName( type, Size.nil(), typeConfiguration );
}
public static String getTypeName(JdbcMappingContainer type, Size size, TypeConfiguration typeConfiguration) {
if ( type instanceof SqlTypedMapping ) {
return AbstractSqlAstTranslator.getSqlTypeName( (SqlTypedMapping) type, walker.getSessionFactory() );
return AbstractSqlAstTranslator.getSqlTypeName( (SqlTypedMapping) type, typeConfiguration );
}
else {
final BasicType<?> basicType = (BasicType<?>) type.getSingleJdbcMapping();
final TypeConfiguration typeConfiguration = walker.getSessionFactory().getTypeConfiguration();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final DdlType ddlType = ddlTypeRegistry.getDescriptor(
basicType.getJdbcType().getDdlTypeCode()
);
return ddlType.getTypeName( Size.nil(), basicType, ddlTypeRegistry );
return ddlType.getTypeName( size, basicType, ddlTypeRegistry );
}
}
public static String getTypeName(ReturnableType<?> type, SqlAstTranslator<?> walker) {
public static String getTypeName(ReturnableType<?> type, TypeConfiguration typeConfiguration) {
return getTypeName( type, Size.nil(), typeConfiguration );
}
public static String getTypeName(ReturnableType<?> type, Size size, TypeConfiguration typeConfiguration) {
if ( type instanceof SqlTypedMapping ) {
return AbstractSqlAstTranslator.getSqlTypeName( (SqlTypedMapping) type, walker.getSessionFactory() );
return AbstractSqlAstTranslator.getSqlTypeName( (SqlTypedMapping) type, typeConfiguration );
}
else {
final BasicType<?> basicType = (BasicType<?>) ( (JdbcMappingContainer) type ).getSingleJdbcMapping();
final TypeConfiguration typeConfiguration = walker.getSessionFactory().getTypeConfiguration();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final DdlType ddlType = ddlTypeRegistry.getDescriptor(
basicType.getJdbcType().getDdlTypeCode()
);
return ddlType.getTypeName( Size.nil(), basicType, ddlTypeRegistry );
return ddlType.getTypeName( size, basicType, ddlTypeRegistry );
}
}
public static String getCastTypeName(BasicType<?> type, SqlAstTranslator<?> walker) {
return getCastTypeName( (JdbcMappingContainer) type, walker );
public static String getCastTypeName(BasicType<?> type, TypeConfiguration typeConfiguration) {
return getCastTypeName( (JdbcMappingContainer) type, typeConfiguration );
}
public static String getCastTypeName(JdbcMappingContainer type, SqlAstTranslator<?> walker) {
public static String getCastTypeName(BasicType<?> type, Size size, TypeConfiguration typeConfiguration) {
return getCastTypeName( (JdbcMappingContainer) type, size, typeConfiguration );
}
public static String getCastTypeName(JdbcMappingContainer type, TypeConfiguration typeConfiguration) {
return getCastTypeName( type, Size.nil(), typeConfiguration );
}
public static String getCastTypeName(JdbcMappingContainer type, Size size, TypeConfiguration typeConfiguration) {
if ( type instanceof SqlTypedMapping ) {
return AbstractSqlAstTranslator.getCastTypeName( (SqlTypedMapping) type, walker.getSessionFactory() );
return AbstractSqlAstTranslator.getCastTypeName( (SqlTypedMapping) type, typeConfiguration );
}
else {
final BasicType<?> basicType = (BasicType<?>) type.getSingleJdbcMapping();
final TypeConfiguration typeConfiguration = walker.getSessionFactory().getTypeConfiguration();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final DdlType ddlType = ddlTypeRegistry.getDescriptor(
basicType.getJdbcType().getDdlTypeCode()
);
return ddlType.getCastTypeName( Size.nil(), basicType, ddlTypeRegistry );
return ddlType.getCastTypeName( size, basicType, ddlTypeRegistry );
}
}
public static String getCastTypeName(ReturnableType<?> type, SqlAstTranslator<?> walker) {
public static String getCastTypeName(ReturnableType<?> type, TypeConfiguration typeConfiguration) {
return getCastTypeName( type, Size.nil(), typeConfiguration );
}
public static String getCastTypeName(ReturnableType<?> type, Size size, TypeConfiguration typeConfiguration) {
if ( type instanceof SqlTypedMapping ) {
return AbstractSqlAstTranslator.getCastTypeName( (SqlTypedMapping) type, walker.getSessionFactory() );
return AbstractSqlAstTranslator.getCastTypeName( (SqlTypedMapping) type, typeConfiguration );
}
else {
final BasicType<?> basicType = (BasicType<?>) ( (JdbcMappingContainer) type ).getSingleJdbcMapping();
final TypeConfiguration typeConfiguration = walker.getSessionFactory().getTypeConfiguration();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final DdlType ddlType = ddlTypeRegistry.getDescriptor(
basicType.getJdbcType().getDdlTypeCode()
);
return ddlType.getCastTypeName( Size.nil(), basicType, ddlTypeRegistry );
return ddlType.getCastTypeName( size, basicType, ddlTypeRegistry );
}
}

View File

@ -29,7 +29,10 @@ public class HSQLArrayConstructorFunction extends ArrayConstructorFunction {
SqlAstTranslator<?> walker) {
final String castTypeName;
if ( returnType != null && hasOnlyBottomArguments( arguments ) ) {
castTypeName = DdlTypeHelper.getCastTypeName( returnType, walker );
castTypeName = DdlTypeHelper.getCastTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( "cast(" );
}
else {

View File

@ -34,7 +34,7 @@ public class OracleArrayConcatElementFunction extends ArrayConcatElementFunction
final String arrayTypeName = DdlTypeHelper.getTypeName(
prepend ? secondArgument.getExpressionType()
: firstArgument.getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_concat(" );

View File

@ -29,7 +29,10 @@ public class OracleArrayConcatFunction extends ArrayConcatFunction {
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName( (JdbcMappingContainer) returnType, walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
(JdbcMappingContainer) returnType,
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_concat" );
super.render( sqlAppender, sqlAstArguments, returnType, walker );

View File

@ -40,7 +40,10 @@ public class OracleArrayConstructorFunction extends ArrayConstructorFunction {
);
}
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) type;
final String arrayTypeName = DdlTypeHelper.getCastTypeName( pluralType, walker );
final String arrayTypeName = DdlTypeHelper.getCastTypeName(
pluralType,
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
final int size = sqlAstArguments.size();
if ( size == 0 ) {

View File

@ -34,7 +34,10 @@ public class OracleArrayContainsFunction extends AbstractArrayContainsFunction {
final Expression needleExpression = (Expression) sqlAstArguments.get( 1 );
final JdbcMappingContainer needleTypeContainer = needleExpression.getExpressionType();
final JdbcMapping needleType = needleTypeContainer == null ? null : needleTypeContainer.getSingleJdbcMapping();
final String arrayTypeName = DdlTypeHelper.getTypeName( haystackExpression.getExpressionType(), walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
haystackExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
if ( needleType == null || needleType instanceof BasicPluralType<?, ?> ) {
sqlAppender.append( "_contains(" );

View File

@ -29,7 +29,10 @@ public class OracleArrayFillFunction extends AbstractArrayFillFunction {
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName( returnType, walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_fill(" );
sqlAstArguments.get( 0 ).accept( walker );

View File

@ -30,7 +30,7 @@ public class OracleArrayGetFunction extends ArrayGetUnnestFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_get(" );

View File

@ -39,7 +39,10 @@ public class OracleArrayLengthFunction extends AbstractSqmSelfRenderingFunctionD
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
final String arrayTypeName = DdlTypeHelper.getTypeName( arrayExpression.getExpressionType(), walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
arrayExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
sqlAppender.append( "_length(" );
arrayExpression.accept( walker );

View File

@ -28,7 +28,10 @@ public class OracleArrayOverlapsFunction extends AbstractArrayOverlapsFunction {
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression haystackExpression = (Expression) sqlAstArguments.get( 0 );
final String arrayTypeName = DdlTypeHelper.getTypeName( haystackExpression.getExpressionType(), walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
haystackExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
sqlAppender.append( "_overlaps(" );
haystackExpression.accept( walker );

View File

@ -28,7 +28,10 @@ public class OracleArrayPositionFunction extends AbstractArrayPositionFunction {
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
final String arrayTypeName = DdlTypeHelper.getTypeName( arrayExpression.getExpressionType(), walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
arrayExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
sqlAppender.append( "_position(" );
arrayExpression.accept( walker );

View File

@ -28,7 +28,10 @@ public class OracleArrayPositionsFunction extends AbstractArrayPositionsFunction
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
final String arrayTypeName = DdlTypeHelper.getTypeName( arrayExpression.getExpressionType(), walker );
final String arrayTypeName = DdlTypeHelper.getTypeName(
arrayExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.appendSql( arrayTypeName );
sqlAppender.append( "_positions(" );
arrayExpression.accept( walker );

View File

@ -30,7 +30,7 @@ public class OracleArrayRemoveFunction extends AbstractArrayRemoveFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_remove(" );

View File

@ -31,7 +31,7 @@ public class OracleArrayRemoveIndexFunction extends ArrayRemoveIndexUnnestFuncti
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_remove_index(" );

View File

@ -27,7 +27,7 @@ public class OracleArrayReplaceFunction extends ArrayReplaceUnnestFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_replace(" );

View File

@ -30,7 +30,7 @@ public class OracleArraySetFunction extends ArraySetUnnestFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_set(" );

View File

@ -31,7 +31,7 @@ public class OracleArraySliceFunction extends ArraySliceUnnestFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_slice(" );

View File

@ -39,7 +39,7 @@ public class OracleArrayToStringFunction extends ArrayToStringFunction {
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_to_string(" );

View File

@ -9,37 +9,15 @@ package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.ANY;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
/**
* Oracle array_trim function.
*/
public class OracleArrayTrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public OracleArrayTrimFunction() {
super(
"array_trim",
StandardArgumentsValidators.composite(
new ArgumentTypesValidator( null, ANY, INTEGER ),
ArrayArgumentValidator.DEFAULT_INSTANCE
),
ArrayViaArgumentReturnTypeResolver.DEFAULT_INSTANCE,
StandardFunctionArgumentTypeResolvers.composite(
StandardFunctionArgumentTypeResolvers.invariant( ANY, INTEGER ),
StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE
)
);
}
public class OracleArrayTrimFunction extends AbstractArrayTrimFunction {
@Override
public void render(
@ -49,7 +27,7 @@ public class OracleArrayTrimFunction extends AbstractSqmSelfRenderingFunctionDes
SqlAstTranslator<?> walker) {
final String arrayTypeName = DdlTypeHelper.getTypeName(
( (Expression) sqlAstArguments.get( 0 ) ).getExpressionType(),
walker
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( arrayTypeName );
sqlAppender.append( "_trim(" );

View File

@ -8,8 +8,11 @@ package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.SqlTypedMapping;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
@ -47,44 +50,40 @@ public class PostgreSQLArrayConcatElementFunction extends ArrayConcatElementFunc
}
final String elementCastType;
if ( needsElementCasting( elementArgument ) ) {
final JdbcMapping elementType = elementArgument.getExpressionType().getSingleJdbcMapping();
if ( elementType instanceof BottomType ) {
elementCastType = DdlTypeHelper.getCastTypeName(
( (BasicPluralType<?, ?>) arrayArgument.getExpressionType().getSingleJdbcMapping() )
.getElementType(),
walker
);
}
else {
elementCastType = DdlTypeHelper.getCastTypeName( elementType, walker );
}
final JdbcMappingContainer arrayType = arrayArgument.getExpressionType();
final Size size = arrayType instanceof SqlTypedMapping ? ( (SqlTypedMapping) arrayType ).toSize() : null;
elementCastType = DdlTypeHelper.getCastTypeName(
( (BasicPluralType<?, ?>) returnType ).getElementType(),
size,
walker.getSessionFactory().getTypeConfiguration()
);
}
else {
elementCastType = null;
}
sqlAppender.append( "case when " );
arrayArgument.accept( walker );
walker.render( arrayArgument, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( " is not null then " );
if ( prepend && elementCastType != null) {
sqlAppender.append( "cast(" );
firstArgument.accept( walker );
walker.render( firstArgument, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( " as " );
sqlAppender.append( elementCastType );
sqlAppender.append( ')' );
}
else {
firstArgument.accept( walker );
walker.render( firstArgument, SqlAstNodeRenderingMode.DEFAULT );
}
sqlAppender.append( "||" );
if ( !prepend && elementCastType != null) {
sqlAppender.append( "cast(" );
secondArgument.accept( walker );
walker.render( secondArgument, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( " as " );
sqlAppender.append( elementCastType );
sqlAppender.append( ')' );
}
else {
secondArgument.accept( walker );
walker.render( secondArgument, SqlAstNodeRenderingMode.DEFAULT );
}
sqlAppender.append( " end" );
}

View File

@ -39,7 +39,10 @@ public class PostgreSQLArrayConstructorFunction extends ArrayConstructorFunction
if ( type instanceof BasicPluralType<?, ?> ) {
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) type;
if ( needsArrayCasting( pluralType.getElementType() ) ) {
arrayTypeName = DdlTypeHelper.getCastTypeName( pluralType, walker );
arrayTypeName = DdlTypeHelper.getCastTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( "cast(" );
}
}

View File

@ -16,7 +16,7 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
/**
* Implement the array get function by using {@code unnest}.
* Custom casting for the array fill function.
*/
public class PostgreSQLArrayFillFunction extends AbstractArrayFillFunction {
@ -34,7 +34,10 @@ public class PostgreSQLArrayFillFunction extends AbstractArrayFillFunction {
final String elementCastType;
final Expression elementExpression = (Expression) sqlAstArguments.get( 0 );
if ( needsElementCasting( elementExpression ) ) {
elementCastType = DdlTypeHelper.getCastTypeName( elementExpression.getExpressionType(), walker );
elementCastType = DdlTypeHelper.getCastTypeName(
elementExpression.getExpressionType(),
walker.getSessionFactory().getTypeConfiguration()
);
sqlAppender.append( "cast(" );
}
else {

View File

@ -9,6 +9,7 @@ package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
@ -35,9 +36,9 @@ public class PostgreSQLArrayPositionFunction extends AbstractArrayPositionFuncti
sqlAppender.append( "case when " );
arrayExpression.accept( walker );
sqlAppender.append( " is not null then coalesce(array_position(" );
arrayExpression.accept( walker );
walker.render( arrayExpression, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( ',' );
elementExpression.accept( walker );
walker.render( elementExpression, SqlAstNodeRenderingMode.DEFAULT );
if ( sqlAstArguments.size() > 2 ) {
sqlAppender.append( ',' );
sqlAstArguments.get( 2 ).accept( walker );

View File

@ -9,6 +9,7 @@ package org.hibernate.dialect.function.array;
import java.util.List;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
@ -33,9 +34,9 @@ public class PostgreSQLArrayPositionsFunction extends AbstractArrayPositionsFunc
final Expression arrayExpression = (Expression) sqlAstArguments.get( 0 );
final Expression elementExpression = (Expression) sqlAstArguments.get( 1 );
sqlAppender.append( "array_positions(" );
arrayExpression.accept( walker );
walker.render( arrayExpression, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( ',' );
elementExpression.accept( walker );
walker.render( elementExpression, SqlAstNodeRenderingMode.DEFAULT );
sqlAppender.append( ')' );
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.dialect.function.array;
import java.util.List;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
/**
* PostgreSQL array_trim emulation, since the function was only introduced in version 14.
*/
public class PostgreSQLArrayTrimEmulation extends AbstractArrayTrimFunction {
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final SqlAstNode arrayExpression = sqlAstArguments.get( 0 );
final SqlAstNode elementCountExpression = sqlAstArguments.get( 1 );
sqlAppender.append( "coalesce((select array_agg(t.val order by t.idx) from unnest(" );
arrayExpression.accept( walker );
sqlAppender.append( ") with ordinality t(val,idx) where t.idx<=cardinality(" );
arrayExpression.accept( walker );
sqlAppender.append( ")-" );
elementCountExpression.accept( walker );
String arrayTypeName = null;
if ( returnType != null ) {
final DomainType<?> type = returnType.getSqmType();
if ( type instanceof BasicPluralType<?, ?> ) {
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) type;
if ( needsArrayCasting( pluralType.getElementType() ) ) {
arrayTypeName = DdlTypeHelper.getCastTypeName(
returnType,
walker.getSessionFactory().getTypeConfiguration()
);
}
}
}
if ( arrayTypeName != null ) {
sqlAppender.append( "),cast(array[] as " );
sqlAppender.appendSql( arrayTypeName );
sqlAppender.appendSql( "))" );
}
else {
sqlAppender.append( "),array[])" );
}
}
private static boolean needsArrayCasting(BasicType<?> elementType) {
// PostgreSQL doesn't do implicit conversion between text[] and varchar[], so we need casting
return elementType.getJdbcType().isString();
}
}

View File

@ -5830,11 +5830,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( queryParameter );
BindableType<?> paramType = binding.getBindType();
final boolean bindingTypeExplicit;
bindingTypeExplicit = binding.getExplicitTemporalPrecision() != null;
if ( paramType == null ) {
paramType = queryParameter.getHibernateType();
if ( paramType == null ) {
paramType = sqmParameter.getAnticipatedType();
}
// bindingTypeExplicit = false;
}
else {
// bindingTypeExplicit = binding.getExplicitTemporalPrecision() != null;
}
if ( paramType == null ) {
@ -5852,8 +5858,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( inferredValueMapping instanceof ModelPart ) {
final JdbcMapping paramJdbcMapping = paramModelType.getSingleJdbcMapping();
final JdbcMapping inferredJdbcMapping = inferredValueMapping.getSingleJdbcMapping();
// If the bind type has a different JDBC type, we prefer that
if ( paramJdbcMapping.getJdbcType() == inferredJdbcMapping.getJdbcType() ) {
// Only use the inferred mapping as parameter type when the JavaType accepts values of the bind type
if ( inferredJdbcMapping.getMappedJavaType().isWider( paramJdbcMapping.getMappedJavaType() )
// and the bind type is not explicit or the bind type has the same JDBC type
&& ( !bindingTypeExplicit || paramJdbcMapping.getJdbcType() == inferredJdbcMapping.getJdbcType() ) ) {
return resolveInferredValueMappingForParameter( inferredValueMapping );
}
}

View File

@ -218,6 +218,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.sql.DdlType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND;
import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst;
@ -6248,16 +6249,24 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
@Override
public void visitCastTarget(CastTarget castTarget) {
appendSql( getCastTypeName( castTarget, sessionFactory ) );
appendSql( getCastTypeName( castTarget, sessionFactory.getTypeConfiguration() ) );
}
/**
* @deprecated Use {@link #getSqlTypeName(SqlTypedMapping, TypeConfiguration)} instead
*/
@Deprecated(forRemoval = true)
public static String getSqlTypeName(SqlTypedMapping castTarget, SessionFactoryImplementor factory) {
return getSqlTypeName( castTarget, factory.getTypeConfiguration() );
}
public static String getSqlTypeName(SqlTypedMapping castTarget, TypeConfiguration typeConfiguration) {
if ( castTarget.getColumnDefinition() != null ) {
return castTarget.getColumnDefinition();
}
else {
final Size castTargetSize = castTarget.toSize();
final DdlTypeRegistry ddlTypeRegistry = factory.getTypeConfiguration().getDdlTypeRegistry();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final BasicType<?> expressionType = (BasicType<?>) castTarget.getJdbcMapping();
DdlType ddlType = ddlTypeRegistry.getDescriptor( expressionType.getJdbcType().getDdlTypeCode() );
if ( ddlType == null ) {
@ -6270,13 +6279,21 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
}
/**
* @deprecated Use {@link #getCastTypeName(SqlTypedMapping, TypeConfiguration)} instead
*/
@Deprecated(forRemoval = true)
public static String getCastTypeName(SqlTypedMapping castTarget, SessionFactoryImplementor factory) {
return getCastTypeName( castTarget, factory.getTypeConfiguration() );
}
public static String getCastTypeName(SqlTypedMapping castTarget, TypeConfiguration typeConfiguration) {
if ( castTarget.getColumnDefinition() != null ) {
return castTarget.getColumnDefinition();
}
else {
final Size castTargetSize = castTarget.toSize();
final DdlTypeRegistry ddlTypeRegistry = factory.getTypeConfiguration().getDdlTypeRegistry();
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
final BasicType<?> expressionType = (BasicType<?>) castTarget.getJdbcMapping();
DdlType ddlType = ddlTypeRegistry.getDescriptor( expressionType.getJdbcType().getDdlTypeCode() );
if ( ddlType == null ) {

View File

@ -50,6 +50,12 @@ public abstract class AbstractArrayJavaType<T, E> extends AbstractClassJavaType<
);
}
@Override
public boolean isWider(JavaType<?> javaType) {
// Support binding single element value
return this == javaType || componentJavaType == javaType;
}
@Override
public BasicType<?> resolveType(
TypeConfiguration typeConfiguration,

View File

@ -86,6 +86,12 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
return semantics;
}
@Override
public boolean isWider(JavaType<?> javaType) {
// Support binding single element value
return this == javaType || componentJavaType == javaType;
}
@Override
public BasicType<?> resolveType(
TypeConfiguration typeConfiguration,

View File

@ -11,6 +11,7 @@ import java.util.Collection;
import java.util.List;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.sqm.NodeBuilder;
@ -22,6 +23,7 @@ import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -87,6 +89,7 @@ public class ArrayTrimTest {
}
@Test
@SkipForDialect(dialectClass = PostgreSQLDialect.class, majorVersion = 12, reason = "The PostgreSQL emulation for version < 14 doesn't throw an error")
public void testTrimOutOfRange(SessionFactoryScope scope) {
scope.inSession( em -> {
try {