Skip to content

Commit 2cb4652

Browse files
committed
HHH-16780 Add array_agg and array constructor function
1 parent d8bad73 commit 2cb4652

File tree

87 files changed

+2553
-320
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+2553
-320
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

+2
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
461461
functionFactory.listagg_stringAgg( "string" );
462462
functionFactory.inverseDistributionOrderedSetAggregates();
463463
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
464+
functionFactory.array_casting();
465+
functionFactory.arrayAggregate();
464466

465467
functionContributions.getFunctionRegistry().register(
466468
"trunc",

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
4545
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
4646
import org.hibernate.dialect.unique.UniqueDelegate;
47+
import org.hibernate.engine.jdbc.Size;
4748
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
4849
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
4950
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
@@ -260,7 +261,8 @@ public boolean supportsDistinctFromPredicate() {
260261
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
261262
super.initializeFunctionRegistry(functionContributions);
262263

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

@@ -362,14 +364,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
362364
functionContributions.getTypeConfiguration(),
363365
SqlAstNodeRenderingMode.DEFAULT,
364366
"||",
365-
functionContributions.getTypeConfiguration().getDdlTypeRegistry().getDescriptor( VARCHAR )
367+
ddlTypeRegistry.getDescriptor( VARCHAR )
366368
.getCastTypeName(
369+
Size.nil(),
367370
functionContributions.getTypeConfiguration()
368371
.getBasicTypeRegistry()
369372
.resolve( StandardBasicTypes.STRING ),
370-
null,
371-
null,
372-
null
373+
ddlTypeRegistry
373374
),
374375
true
375376
)

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.hibernate.dialect.sequence.SequenceSupport;
3535
import org.hibernate.dialect.temptable.TemporaryTable;
3636
import org.hibernate.dialect.temptable.TemporaryTableKind;
37+
import org.hibernate.engine.jdbc.Size;
3738
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
3839
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
3940
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
@@ -319,8 +320,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
319320

320321
final BasicTypeRegistry basicTypeRegistry = functionContributions.getTypeConfiguration().getBasicTypeRegistry();
321322
final BasicType<String> stringType = basicTypeRegistry.resolve( StandardBasicTypes.STRING );
322-
323-
CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
323+
final DdlTypeRegistry ddlTypeRegistry = functionContributions.getTypeConfiguration().getDdlTypeRegistry();
324+
final CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
324325

325326
// Derby needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
326327
functionFactory.aggregates( this, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
@@ -331,8 +332,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
331332
functionContributions.getTypeConfiguration(),
332333
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
333334
"||",
334-
functionContributions.getTypeConfiguration().getDdlTypeRegistry().getDescriptor( VARCHAR )
335-
.getCastTypeName( stringType, null, null, null ),
335+
ddlTypeRegistry.getDescriptor( VARCHAR )
336+
.getCastTypeName( Size.nil(), stringType, ddlTypeRegistry ),
336337
true
337338
)
338339
);

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,12 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
366366
functionFactory.rownum();
367367
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
368368
functionFactory.windowFunctions();
369+
functionFactory.inverseDistributionOrderedSetAggregates();
370+
functionFactory.hypotheticalOrderedSetAggregates();
369371
if ( getVersion().isSameOrAfter( 2 ) ) {
370372
functionFactory.listagg( null );
371-
functionFactory.inverseDistributionOrderedSetAggregates();
372-
functionFactory.hypotheticalOrderedSetAggregates();
373+
functionFactory.array();
374+
functionFactory.arrayAggregate();
373375
}
374376
else {
375377
// Use group_concat until 2.x as listagg was buggy

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java

+2
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
247247
functionFactory.rownum();
248248
}
249249
functionFactory.listagg_groupConcat();
250+
functionFactory.array();
251+
functionFactory.arrayAggregate();
250252
}
251253

252254
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java

+39-12
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,14 @@
105105
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
106106
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
107107
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
108+
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
108109
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
109110
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
110111
import org.hibernate.type.spi.TypeConfiguration;
111112

112113
import jakarta.persistence.TemporalType;
113114

115+
import static java.util.regex.Pattern.CASE_INSENSITIVE;
114116
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
115117
import static org.hibernate.query.sqm.TemporalUnit.DAY;
116118
import static org.hibernate.query.sqm.TemporalUnit.HOUR;
@@ -124,6 +126,8 @@
124126
import static org.hibernate.type.SqlTypes.BOOLEAN;
125127
import static org.hibernate.type.SqlTypes.DATE;
126128
import static org.hibernate.type.SqlTypes.DECIMAL;
129+
import static org.hibernate.type.SqlTypes.DOUBLE;
130+
import static org.hibernate.type.SqlTypes.FLOAT;
127131
import static org.hibernate.type.SqlTypes.GEOMETRY;
128132
import static org.hibernate.type.SqlTypes.INTEGER;
129133
import static org.hibernate.type.SqlTypes.JSON;
@@ -133,6 +137,7 @@
133137
import static org.hibernate.type.SqlTypes.SMALLINT;
134138
import static org.hibernate.type.SqlTypes.SQLXML;
135139
import static org.hibernate.type.SqlTypes.STRUCT;
140+
import static org.hibernate.type.SqlTypes.TABLE;
136141
import static org.hibernate.type.SqlTypes.TIME;
137142
import static org.hibernate.type.SqlTypes.TIMESTAMP;
138143
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
@@ -150,17 +155,20 @@
150155
*/
151156
public class OracleLegacyDialect extends Dialect {
152157

153-
private static final Pattern DISTINCT_KEYWORD_PATTERN = Pattern.compile( "\\bdistinct\\b" );
154-
private static final Pattern GROUP_BY_KEYWORD_PATTERN = Pattern.compile( "\\bgroup\\sby\\b" );
155-
private static final Pattern ORDER_BY_KEYWORD_PATTERN = Pattern.compile( "\\border\\sby\\b" );
156-
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b" );
157-
private static final Pattern SQL_STATEMENT_TYPE_PATTERN = Pattern.compile("^(?:/\\*.*?\\*/)?\\s*(select|insert|update|delete)\\s+.*?", Pattern.CASE_INSENSITIVE);
158+
private static final Pattern DISTINCT_KEYWORD_PATTERN = Pattern.compile( "\\bdistinct\\b", CASE_INSENSITIVE );
159+
private static final Pattern GROUP_BY_KEYWORD_PATTERN = Pattern.compile( "\\bgroup\\s+by\\b", CASE_INSENSITIVE );
160+
private static final Pattern ORDER_BY_KEYWORD_PATTERN = Pattern.compile( "\\border\\s+by\\b", CASE_INSENSITIVE );
161+
private static final Pattern UNION_KEYWORD_PATTERN = Pattern.compile( "\\bunion\\b", CASE_INSENSITIVE );
162+
163+
private static final Pattern SQL_STATEMENT_TYPE_PATTERN =
164+
Pattern.compile( "^(?:/\\*.*?\\*/)?\\s*(select|insert|update|delete)\\s+.*?", CASE_INSENSITIVE );
165+
158166
private static final int PARAM_LIST_SIZE_LIMIT = 1000;
159167

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

162170
private static final String yqmSelect =
163-
"( 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 ) )";
171+
"(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))";
164172

165173
private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
166174
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
@@ -220,6 +228,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
220228
functionFactory.addMonths();
221229
functionFactory.monthsBetween();
222230
functionFactory.everyAny_minMaxCase();
231+
functionFactory.repeat_rpad();
223232

224233
functionFactory.radians_acos();
225234
functionFactory.degrees_acos();
@@ -273,6 +282,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
273282
new OracleTruncFunction( functionContributions.getTypeConfiguration() )
274283
);
275284
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
285+
286+
functionFactory.array_oracle();
287+
functionFactory.arrayAggregate_jsonArrayagg();
276288
}
277289

278290
@Override
@@ -625,6 +637,10 @@ protected String columnType(int sqlTypeCode) {
625637
case REAL:
626638
// Oracle's 'real' type is actually double precision
627639
return "float(24)";
640+
case DOUBLE:
641+
// Oracle's 'double precision' means float(126), and
642+
// we never need 126 bits (38 decimal digits)
643+
return "float(53)";
628644

629645
case NUMERIC:
630646
case DECIMAL:
@@ -670,6 +686,9 @@ else if ( getVersion().isSameOrAfter( 12 ) ) {
670686
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
671687
}
672688
}
689+
690+
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
691+
ddlTypeRegistry.addDescriptor( TABLE, new ArrayDdlTypeImpl( this, false ) );
673692
}
674693

675694
@Override
@@ -736,13 +755,20 @@ public JdbcType resolveSqlTypeDescriptor(
736755
}
737756
break;
738757
case Types.NUMERIC:
739-
if ( scale == -127 ) {
740-
// For some reason, the Oracle JDBC driver reports FLOAT
741-
// as NUMERIC with scale -127
742-
if ( precision <= getFloatPrecision() ) {
743-
return jdbcTypeRegistry.getDescriptor( Types.FLOAT );
758+
if ( precision > 8 // precision of 0 means something funny
759+
// For some reason, the Oracle JDBC driver reports
760+
// FLOAT or DOUBLE as NUMERIC with scale -127
761+
// (but note that expressions with unknown type
762+
// also get reported this way, so take care)
763+
&& scale == -127 ) {
764+
if ( precision <= 24 ) {
765+
// Can be represented as a Java float
766+
return jdbcTypeRegistry.getDescriptor( FLOAT );
767+
}
768+
else if ( precision <= 53 ) {
769+
// Can be represented as a Java double
770+
return jdbcTypeRegistry.getDescriptor( DOUBLE );
744771
}
745-
return jdbcTypeRegistry.getDescriptor( Types.DOUBLE );
746772
}
747773
//intentional fall-through:
748774
case Types.DECIMAL:
@@ -825,6 +851,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
825851

826852
if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
827853
typeContributions.contributeJdbcTypeConstructor( OracleJdbcHelper.getArrayJdbcTypeConstructor( serviceRegistry ) );
854+
typeContributions.contributeJdbcTypeConstructor( OracleJdbcHelper.getNestedTableJdbcTypeConstructor( serviceRegistry ) );
828855
}
829856
else {
830857
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java

+43-27
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.ArrayList;
1010
import java.util.List;
1111

12+
import org.hibernate.dialect.OracleArrayJdbcType;
1213
import org.hibernate.engine.spi.SessionFactoryImplementor;
1314
import org.hibernate.internal.util.collections.Stack;
1415
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
@@ -53,6 +54,7 @@
5354
import org.hibernate.sql.exec.spi.JdbcOperation;
5455
import org.hibernate.sql.results.internal.SqlSelectionImpl;
5556
import org.hibernate.type.SqlTypes;
57+
import org.hibernate.type.descriptor.jdbc.JdbcType;
5658

5759
/**
5860
* A SQL AST translator for Oracle.
@@ -454,7 +456,8 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
454456
renderComparisonEmulateDecode( lhs, operator, rhs );
455457
return;
456458
}
457-
switch ( lhsExpressionType.getSingleJdbcMapping().getJdbcType().getDdlTypeCode() ) {
459+
final JdbcType jdbcType = lhsExpressionType.getSingleJdbcMapping().getJdbcType();
460+
switch ( jdbcType.getDdlTypeCode() ) {
458461
case SqlTypes.SQLXML:
459462
// In Oracle, XMLTYPE is not "comparable", so we have to use the xmldiff function for this purpose
460463
switch ( operator ) {
@@ -499,25 +502,51 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
499502
appendSql( ')' );
500503
break;
501504
case SqlTypes.ARRAY:
505+
final String arrayTypeName = ( (OracleArrayJdbcType) jdbcType ).getTypeName();
502506
switch ( operator ) {
503507
case DISTINCT_FROM:
504-
appendSql( "decode(" );
505-
arrayToString( lhs );
506-
appendSql( ',' );
507-
arrayToString( rhs );
508-
appendSql( ",0,1)=1" );
509-
break;
510508
case NOT_DISTINCT_FROM:
511-
appendSql( "decode(" );
512-
arrayToString( lhs );
509+
appendSql( arrayTypeName );
510+
appendSql( "_distinct(" );
511+
visitSqlSelectExpression( lhs );
513512
appendSql( ',' );
514-
arrayToString( rhs );
515-
appendSql( ",0,1)=0" );
513+
visitSqlSelectExpression( rhs );
514+
appendSql( ")" );
516515
break;
517516
default:
518-
arrayToString( lhs );
519-
appendSql( operator.sqlText() );
520-
arrayToString( rhs );
517+
appendSql( arrayTypeName );
518+
appendSql( "_cmp(" );
519+
visitSqlSelectExpression( lhs );
520+
appendSql( ',' );
521+
visitSqlSelectExpression( rhs );
522+
appendSql( ")" );
523+
break;
524+
}
525+
switch ( operator ) {
526+
case DISTINCT_FROM:
527+
appendSql( "=1" );
528+
break;
529+
case NOT_DISTINCT_FROM:
530+
appendSql( "=0" );
531+
break;
532+
case EQUAL:
533+
appendSql( "=0" );
534+
break;
535+
case NOT_EQUAL:
536+
appendSql( "<>0" );
537+
break;
538+
case LESS_THAN:
539+
appendSql( "=-1" );
540+
break;
541+
case GREATER_THAN:
542+
appendSql( "=1" );
543+
break;
544+
case LESS_THAN_OR_EQUAL:
545+
appendSql( "<=0" );
546+
break;
547+
case GREATER_THAN_OR_EQUAL:
548+
appendSql( ">=0" );
549+
break;
521550
}
522551
break;
523552
default:
@@ -526,19 +555,6 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
526555
}
527556
}
528557

529-
private void arrayToString(Expression expression) {
530-
appendSql("case when ");
531-
expression.accept( this );
532-
appendSql(" is not null then (select listagg(column_value||',')");
533-
if ( !getDialect().getVersion().isSameOrAfter( 18 ) ) {
534-
// The within group clause became optional in 18
535-
appendSql(" within group(order by rownum)");
536-
}
537-
appendSql("||';' from table(");
538-
expression.accept( this );
539-
appendSql(")) else null end");
540-
}
541-
542558
@Override
543559
protected void renderSelectTupleComparison(
544560
List<SqlSelection> lhsExpressions,

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java

+6
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
8585
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
8686
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
87+
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
8788
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
8889
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
8990
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
@@ -228,6 +229,9 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
228229
super.registerColumnTypes( typeContributions, serviceRegistry );
229230
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
230231

232+
// We need to configure that the array type uses the raw element type for casts
233+
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, true ) );
234+
231235
// Register this type to be able to support Float[]
232236
// The issue is that the JDBC driver can't handle createArrayOf( "float(24)", ... )
233237
// It requires the use of "real" or "float4"
@@ -577,6 +581,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
577581
functionFactory.locate_positionSubstring();
578582
functionFactory.windowFunctions();
579583
functionFactory.listagg_stringAgg( "varchar" );
584+
functionFactory.array_casting();
585+
functionFactory.arrayAggregate();
580586

581587
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
582588
functionFactory.makeDateTimeTimestamp();

hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java

+2
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
448448
functionFactory.listagg_stringAgg( "string" );
449449
functionFactory.inverseDistributionOrderedSetAggregates();
450450
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
451+
functionFactory.array_casting();
452+
functionFactory.arrayAggregate();
451453

452454
functionContributions.getFunctionRegistry().register(
453455
"trunc",

0 commit comments

Comments
 (0)