Skip to content

HHH-16780 Add array_agg and array constructor function #7392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.listagg_stringAgg( "string" );
functionFactory.inverseDistributionOrderedSetAggregates();
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
functionFactory.array_casting();
functionFactory.arrayAggregate();

functionContributions.getFunctionRegistry().register(
"trunc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
Expand Down Expand Up @@ -260,7 +261,8 @@ public boolean supportsDistinctFromPredicate() {
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);

CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
final DdlTypeRegistry ddlTypeRegistry = functionContributions.getTypeConfiguration().getDdlTypeRegistry();
final CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT );

Expand Down Expand Up @@ -362,14 +364,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionContributions.getTypeConfiguration(),
SqlAstNodeRenderingMode.DEFAULT,
"||",
functionContributions.getTypeConfiguration().getDdlTypeRegistry().getDescriptor( VARCHAR )
ddlTypeRegistry.getDescriptor( VARCHAR )
.getCastTypeName(
Size.nil(),
functionContributions.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( StandardBasicTypes.STRING ),
null,
null,
null
ddlTypeRegistry
),
true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
Expand Down Expand Up @@ -319,8 +320,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio

final BasicTypeRegistry basicTypeRegistry = functionContributions.getTypeConfiguration().getBasicTypeRegistry();
final BasicType<String> stringType = basicTypeRegistry.resolve( StandardBasicTypes.STRING );

CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
final DdlTypeRegistry ddlTypeRegistry = functionContributions.getTypeConfiguration().getDdlTypeRegistry();
final CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);

// Derby needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
functionFactory.aggregates( this, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
Expand All @@ -331,8 +332,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionContributions.getTypeConfiguration(),
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
"||",
functionContributions.getTypeConfiguration().getDdlTypeRegistry().getDescriptor( VARCHAR )
.getCastTypeName( stringType, null, null, null ),
ddlTypeRegistry.getDescriptor( VARCHAR )
.getCastTypeName( Size.nil(), stringType, ddlTypeRegistry ),
true
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,12 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.rownum();
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
functionFactory.windowFunctions();
functionFactory.inverseDistributionOrderedSetAggregates();
functionFactory.hypotheticalOrderedSetAggregates();
if ( getVersion().isSameOrAfter( 2 ) ) {
functionFactory.listagg( null );
functionFactory.inverseDistributionOrderedSetAggregates();
functionFactory.hypotheticalOrderedSetAggregates();
functionFactory.array();
functionFactory.arrayAggregate();
}
else {
// Use group_concat until 2.x as listagg was buggy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.rownum();
}
functionFactory.listagg_groupConcat();
functionFactory.array();
functionFactory.arrayAggregate();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,14 @@
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;

import jakarta.persistence.TemporalType;

import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.HOUR;
Expand All @@ -124,6 +126,8 @@
import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.DATE;
import static org.hibernate.type.SqlTypes.DECIMAL;
import static org.hibernate.type.SqlTypes.DOUBLE;
import static org.hibernate.type.SqlTypes.FLOAT;
import static org.hibernate.type.SqlTypes.GEOMETRY;
import static org.hibernate.type.SqlTypes.INTEGER;
import static org.hibernate.type.SqlTypes.JSON;
Expand All @@ -133,6 +137,7 @@
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.SQLXML;
import static org.hibernate.type.SqlTypes.STRUCT;
import static org.hibernate.type.SqlTypes.TABLE;
import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
Expand All @@ -150,17 +155,20 @@
*/
public class OracleLegacyDialect extends Dialect {

private static final Pattern DISTINCT_KEYWORD_PATTERN = Pattern.compile( "\\bdistinct\\b" );
private static final Pattern GROUP_BY_KEYWORD_PATTERN = Pattern.compile( "\\bgroup\\sby\\b" );
private static final Pattern ORDER_BY_KEYWORD_PATTERN = Pattern.compile( "\\border\\sby\\b" );
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b" );
private static final Pattern SQL_STATEMENT_TYPE_PATTERN = Pattern.compile("^(?:/\\*.*?\\*/)?\\s*(select|insert|update|delete)\\s+.*?", Pattern.CASE_INSENSITIVE);
private static final Pattern DISTINCT_KEYWORD_PATTERN = Pattern.compile( "\\bdistinct\\b", CASE_INSENSITIVE );
private static final Pattern GROUP_BY_KEYWORD_PATTERN = Pattern.compile( "\\bgroup\\s+by\\b", CASE_INSENSITIVE );
private static final Pattern ORDER_BY_KEYWORD_PATTERN = Pattern.compile( "\\border\\s+by\\b", CASE_INSENSITIVE );
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b", CASE_INSENSITIVE );

private static final Pattern SQL_STATEMENT_TYPE_PATTERN =
Pattern.compile( "^(?:/\\*.*?\\*/)?\\s*(select|insert|update|delete)\\s+.*?", CASE_INSENSITIVE );

private static final int PARAM_LIST_SIZE_LIMIT = 1000;

public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";

private static final String yqmSelect =
"( TRUNC(%2$s, 'MONTH') + NUMTOYMINTERVAL(%1$s, 'MONTH') + ( LEAST( EXTRACT( DAY FROM %2$s ), EXTRACT( DAY FROM LAST_DAY( TRUNC(%2$s, 'MONTH') + NUMTOYMINTERVAL(%1$s, 'MONTH') ) ) ) - 1 ) )";
"(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH') + (least(extract(day from %2$s), extract(day from last_day(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH')))) - 1))";

private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
Expand Down Expand Up @@ -220,6 +228,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.addMonths();
functionFactory.monthsBetween();
functionFactory.everyAny_minMaxCase();
functionFactory.repeat_rpad();

functionFactory.radians_acos();
functionFactory.degrees_acos();
Expand Down Expand Up @@ -273,6 +282,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
new OracleTruncFunction( functionContributions.getTypeConfiguration() )
);
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );

functionFactory.array_oracle();
functionFactory.arrayAggregate_jsonArrayagg();
}

@Override
Expand Down Expand Up @@ -625,6 +637,10 @@ protected String columnType(int sqlTypeCode) {
case REAL:
// Oracle's 'real' type is actually double precision
return "float(24)";
case DOUBLE:
// Oracle's 'double precision' means float(126), and
// we never need 126 bits (38 decimal digits)
return "float(53)";

case NUMERIC:
case DECIMAL:
Expand Down Expand Up @@ -670,6 +686,9 @@ else if ( getVersion().isSameOrAfter( 12 ) ) {
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
}
}

ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
ddlTypeRegistry.addDescriptor( TABLE, new ArrayDdlTypeImpl( this, false ) );
}

@Override
Expand Down Expand Up @@ -736,13 +755,20 @@ public JdbcType resolveSqlTypeDescriptor(
}
break;
case Types.NUMERIC:
if ( scale == -127 ) {
// For some reason, the Oracle JDBC driver reports FLOAT
// as NUMERIC with scale -127
if ( precision <= getFloatPrecision() ) {
return jdbcTypeRegistry.getDescriptor( Types.FLOAT );
if ( precision > 8 // precision of 0 means something funny
// For some reason, the Oracle JDBC driver reports
// FLOAT or DOUBLE as NUMERIC with scale -127
// (but note that expressions with unknown type
// also get reported this way, so take care)
&& scale == -127 ) {
if ( precision <= 24 ) {
// Can be represented as a Java float
return jdbcTypeRegistry.getDescriptor( FLOAT );
}
else if ( precision <= 53 ) {
// Can be represented as a Java double
return jdbcTypeRegistry.getDescriptor( DOUBLE );
}
return jdbcTypeRegistry.getDescriptor( Types.DOUBLE );
}
//intentional fall-through:
case Types.DECIMAL:
Expand Down Expand Up @@ -825,6 +851,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry

if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
typeContributions.contributeJdbcTypeConstructor( OracleJdbcHelper.getArrayJdbcTypeConstructor( serviceRegistry ) );
typeContributions.contributeJdbcTypeConstructor( OracleJdbcHelper.getNestedTableJdbcTypeConstructor( serviceRegistry ) );
}
else {
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.List;

import org.hibernate.dialect.OracleArrayJdbcType;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
Expand Down Expand Up @@ -53,6 +54,7 @@
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;

/**
* A SQL AST translator for Oracle.
Expand Down Expand Up @@ -454,7 +456,8 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
renderComparisonEmulateDecode( lhs, operator, rhs );
return;
}
switch ( lhsExpressionType.getSingleJdbcMapping().getJdbcType().getDdlTypeCode() ) {
final JdbcType jdbcType = lhsExpressionType.getSingleJdbcMapping().getJdbcType();
switch ( jdbcType.getDdlTypeCode() ) {
case SqlTypes.SQLXML:
// In Oracle, XMLTYPE is not "comparable", so we have to use the xmldiff function for this purpose
switch ( operator ) {
Expand Down Expand Up @@ -499,25 +502,51 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
appendSql( ')' );
break;
case SqlTypes.ARRAY:
final String arrayTypeName = ( (OracleArrayJdbcType) jdbcType ).getTypeName();
switch ( operator ) {
case DISTINCT_FROM:
appendSql( "decode(" );
arrayToString( lhs );
appendSql( ',' );
arrayToString( rhs );
appendSql( ",0,1)=1" );
break;
case NOT_DISTINCT_FROM:
appendSql( "decode(" );
arrayToString( lhs );
appendSql( arrayTypeName );
appendSql( "_distinct(" );
visitSqlSelectExpression( lhs );
appendSql( ',' );
arrayToString( rhs );
appendSql( ",0,1)=0" );
visitSqlSelectExpression( rhs );
appendSql( ")" );
break;
default:
arrayToString( lhs );
appendSql( operator.sqlText() );
arrayToString( rhs );
appendSql( arrayTypeName );
appendSql( "_cmp(" );
visitSqlSelectExpression( lhs );
appendSql( ',' );
visitSqlSelectExpression( rhs );
appendSql( ")" );
break;
}
switch ( operator ) {
case DISTINCT_FROM:
appendSql( "=1" );
break;
case NOT_DISTINCT_FROM:
appendSql( "=0" );
break;
case EQUAL:
appendSql( "=0" );
break;
case NOT_EQUAL:
appendSql( "<>0" );
break;
case LESS_THAN:
appendSql( "=-1" );
break;
case GREATER_THAN:
appendSql( "=1" );
break;
case LESS_THAN_OR_EQUAL:
appendSql( "<=0" );
break;
case GREATER_THAN_OR_EQUAL:
appendSql( ">=0" );
break;
}
break;
default:
Expand All @@ -526,19 +555,6 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
}
}

private void arrayToString(Expression expression) {
appendSql("case when ");
expression.accept( this );
appendSql(" is not null then (select listagg(column_value||',')");
if ( !getDialect().getVersion().isSameOrAfter( 18 ) ) {
// The within group clause became optional in 18
appendSql(" within group(order by rownum)");
}
appendSql("||';' from table(");
expression.accept( this );
appendSql(")) else null end");
}

@Override
protected void renderSelectTupleComparison(
List<SqlSelection> lhsExpressions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
Expand Down Expand Up @@ -228,6 +229,9 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
super.registerColumnTypes( typeContributions, serviceRegistry );
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();

// We need to configure that the array type uses the raw element type for casts
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, true ) );

// Register this type to be able to support Float[]
// The issue is that the JDBC driver can't handle createArrayOf( "float(24)", ... )
// It requires the use of "real" or "float4"
Expand Down Expand Up @@ -577,6 +581,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.locate_positionSubstring();
functionFactory.windowFunctions();
functionFactory.listagg_stringAgg( "varchar" );
functionFactory.array_casting();
functionFactory.arrayAggregate();

if ( getVersion().isSameOrAfter( 9, 4 ) ) {
functionFactory.makeDateTimeTimestamp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
functionFactory.listagg_stringAgg( "string" );
functionFactory.inverseDistributionOrderedSetAggregates();
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
functionFactory.array_casting();
functionFactory.arrayAggregate();

functionContributions.getFunctionRegistry().register(
"trunc",
Expand Down
Loading