Skip to content

Commit ae7a098

Browse files
committed
HHH-18661 Add unnest() set-returning function and enable XML/JSON based array support on more databases
1 parent 968a661 commit ae7a098

File tree

213 files changed

+7702
-818
lines changed

Some content is hidden

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

213 files changed

+7702
-818
lines changed

docker_db.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ use master
434434
go
435435
create login $SYBASE_USER with password $SYBASE_PASSWORD
436436
go
437+
exec sp_configure 'enable xml', 1
438+
go
437439
exec sp_dboption $SYBASE_DB, 'abort tran on log full', true
438440
go
439441
exec sp_dboption $SYBASE_DB, 'allow nulls by default', true

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@
9292
import static org.hibernate.type.SqlTypes.INET;
9393
import static org.hibernate.type.SqlTypes.INTEGER;
9494
import static org.hibernate.type.SqlTypes.JSON;
95-
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
9695
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
9796
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
9897
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
@@ -263,11 +262,9 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
263262
if ( getVersion().isSameOrAfter( 20 ) ) {
264263
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
265264
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
266-
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "jsonb", this ) );
267265
}
268266
else {
269267
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
270-
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
271268
}
272269
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
273270
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
@@ -372,23 +369,23 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser
372369
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
373370
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
374371
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
375-
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
372+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
376373
}
377374
else {
378375
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
379-
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
376+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
380377
}
381378
}
382379
else {
383380
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
384381
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
385382
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
386383
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
387-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
384+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
388385
}
389386
else {
390387
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
391-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
388+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
392389
}
393390
}
394391
}
@@ -398,11 +395,11 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser
398395
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
399396
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
400397
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
401-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
398+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
402399
}
403400
else {
404401
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
405-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
402+
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
406403
}
407404
}
408405

@@ -424,6 +421,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser
424421
)
425422
);
426423

424+
// Replace the standard array constructor
427425
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
428426
}
429427

@@ -518,6 +516,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
518516
functionFactory.jsonArrayAppend_postgresql( false );
519517
functionFactory.jsonArrayInsert_postgresql();
520518

519+
functionFactory.unnest_postgresql();
520+
521521
// Postgres uses # instead of ^ for XOR
522522
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
523523
.setExactArgumentCount( 2 )

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
8989
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
9090
import org.hibernate.type.JavaObjectType;
91+
import org.hibernate.type.SqlTypes;
9192
import org.hibernate.type.StandardBasicTypes;
9293
import org.hibernate.type.descriptor.ValueExtractor;
9394
import org.hibernate.type.descriptor.java.JavaType;
@@ -455,6 +456,14 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
455456
functionFactory.xmlexists_db2_legacy();
456457
}
457458
functionFactory.xmlagg();
459+
460+
functionFactory.unnest_emulated();
461+
}
462+
463+
@Override
464+
public int getPreferredSqlTypeCodeForArray() {
465+
// Even if DB2 11 supports JSON functions, it's not possible to unnest a JSON array to rows, so stick to XML
466+
return SqlTypes.XML_ARRAY;
458467
}
459468

460469
@Override

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.hibernate.sql.ast.tree.expression.Literal;
2929
import org.hibernate.sql.ast.tree.expression.SqlTuple;
3030
import org.hibernate.sql.ast.tree.expression.Summarization;
31+
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
32+
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
3133
import org.hibernate.sql.ast.tree.from.NamedTableReference;
3234
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
3335
import org.hibernate.sql.ast.tree.from.TableGroup;
@@ -253,6 +255,17 @@ public void visitQueryPartTableReference(QueryPartTableReference tableReference)
253255
inLateral = oldLateral;
254256
}
255257

258+
@Override
259+
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
260+
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
261+
// No need for a lateral keyword for functions
262+
tableReference.accept( this );
263+
}
264+
else {
265+
super.renderDerivedTableReference( tableReference );
266+
}
267+
}
268+
256269
@Override
257270
public void visitSelectStatement(SelectStatement statement) {
258271
if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) {

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

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
*/
55
package org.hibernate.community.dialect;
66

7-
import org.hibernate.LockMode;
87
import org.hibernate.dialect.DatabaseVersion;
98
import org.hibernate.engine.spi.SessionFactoryImplementor;
109
import org.hibernate.query.sqm.ComparisonOperator;
1110
import org.hibernate.sql.ast.tree.Statement;
1211
import org.hibernate.sql.ast.tree.expression.Expression;
1312
import org.hibernate.sql.ast.tree.expression.Literal;
14-
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
15-
import org.hibernate.sql.ast.tree.from.NamedTableReference;
16-
import org.hibernate.sql.ast.tree.from.TableGroup;
17-
import org.hibernate.sql.ast.tree.from.TableReference;
13+
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
1814
import org.hibernate.sql.ast.tree.select.QueryPart;
1915
import org.hibernate.sql.exec.spi.JdbcOperation;
2016

@@ -58,28 +54,10 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
5854
}
5955

6056
@Override
61-
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
62-
if ( shouldInlineCte( tableGroup ) ) {
63-
inlineCteTableGroup( tableGroup, lockMode );
64-
return false;
65-
}
66-
final TableReference tableReference = tableGroup.getPrimaryTableReference();
67-
if ( tableReference instanceof NamedTableReference ) {
68-
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
69-
}
57+
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
7058
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
7159
append( "table " );
72-
tableReference.accept( this );
73-
return false;
74-
}
75-
76-
@Override
77-
public void visitFunctionTableReference(FunctionTableReference tableReference) {
78-
// For the table qualifier we need parenthesis on DB2 z/OS
79-
append( OPEN_PARENTHESIS );
80-
tableReference.getFunctionExpression().accept( this );
81-
append( CLOSE_PARENTHESIS );
82-
renderDerivedTableReference( tableReference );
60+
super.visitQueryPartTableReference( tableReference );
8361
}
8462

8563
@Override

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@
9797
import static org.hibernate.type.SqlTypes.GEOMETRY;
9898
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
9999
import static org.hibernate.type.SqlTypes.JSON;
100-
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
101100
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
102101
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
103102
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
@@ -265,7 +264,6 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
265264
}
266265
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
267266
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
268-
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
269267
}
270268
}
271269
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
@@ -296,7 +294,8 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
296294
}
297295
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
298296
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
299-
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonArrayJdbcType.INSTANCE );
297+
// Replace the standard array constructor
298+
jdbcTypeRegistry.addTypeConstructor( H2JsonArrayJdbcTypeConstructor.INSTANCE );
300299
}
301300
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
302301
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
@@ -427,6 +426,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
427426
else {
428427
functionFactory.listagg_groupConcat();
429428
}
429+
430+
functionFactory.unnest_h2( getMaximumArraySize() );
430431
}
431432

432433
/**

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -488,18 +488,28 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
488488
typeConfiguration
489489
);
490490

491-
if ( getVersion().isSameOrAfter(2, 0, 20) ) {
492-
// Introduced in 2.0 SPS 02
491+
if ( getVersion().isSameOrAfter( 2, 0 ) ) {
492+
// Introduced in 2.0 SPS 00
493493
functionFactory.jsonValue_no_passing();
494494
functionFactory.jsonQuery_no_passing();
495495
functionFactory.jsonExists_hana();
496-
if ( getVersion().isSameOrAfter(2, 0, 40) ) {
497-
// Introduced in 2.0 SPS 04
498-
functionFactory.jsonObject_hana();
499-
functionFactory.jsonArray_hana();
500-
functionFactory.jsonArrayAgg_hana();
501-
functionFactory.jsonObjectAgg_hana();
496+
497+
functionFactory.unnest_hana();
498+
// functionFactory.json_table();
499+
500+
if ( getVersion().isSameOrAfter(2, 0, 20 ) ) {
501+
if ( getVersion().isSameOrAfter( 2, 0, 40 ) ) {
502+
// Introduced in 2.0 SPS 04
503+
functionFactory.jsonObject_hana();
504+
functionFactory.jsonArray_hana();
505+
functionFactory.jsonArrayAgg_hana();
506+
functionFactory.jsonObjectAgg_hana();
507+
}
508+
509+
// functionFactory.xmltable();
502510
}
511+
512+
// functionFactory.xmlextract();
503513
}
504514
}
505515

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

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.hibernate.sql.ast.tree.expression.Expression;
2121
import org.hibernate.sql.ast.tree.expression.Literal;
2222
import org.hibernate.sql.ast.tree.expression.Summarization;
23-
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
2423
import org.hibernate.sql.ast.tree.from.NamedTableReference;
2524
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
2625
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
@@ -34,6 +33,8 @@
3433
import org.hibernate.sql.exec.spi.JdbcOperation;
3534
import org.hibernate.sql.model.internal.TableInsertStandard;
3635

36+
import static org.hibernate.dialect.SybaseASESqlAstTranslator.isLob;
37+
3738
/**
3839
* An SQL AST translator for the Legacy HANA dialect.
3940
*/
@@ -197,12 +198,6 @@ protected SqlAstNodeRenderingMode getParameterRenderingMode() {
197198
return inLateral ? SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS : super.getParameterRenderingMode();
198199
}
199200

200-
@Override
201-
public void visitFunctionTableReference(FunctionTableReference tableReference) {
202-
tableReference.getFunctionExpression().accept( this );
203-
renderTableReferenceIdentificationVariable( tableReference );
204-
}
205-
206201
@Override
207202
public void visitOffsetFetchClause(QueryPart queryPart) {
208203
if ( !isRowNumberingCurrentQueryPart() ) {
@@ -212,15 +207,63 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
212207

213208
@Override
214209
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
210+
// In SAP HANA, LOBs are not "comparable", so we have to use a like predicate for comparison
211+
final boolean isLob = isLob( lhs.getExpressionType() );
215212
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
213+
if ( isLob ) {
214+
switch ( operator ) {
215+
case DISTINCT_FROM:
216+
appendSql( "case when " );
217+
lhs.accept( this );
218+
appendSql( " like " );
219+
rhs.accept( this );
220+
appendSql( " or " );
221+
lhs.accept( this );
222+
appendSql( " is null and " );
223+
rhs.accept( this );
224+
appendSql( " is null then 0 else 1 end=1" );
225+
return;
226+
case NOT_DISTINCT_FROM:
227+
appendSql( "case when " );
228+
lhs.accept( this );
229+
appendSql( " like " );
230+
rhs.accept( this );
231+
appendSql( " or " );
232+
lhs.accept( this );
233+
appendSql( " is null and " );
234+
rhs.accept( this );
235+
appendSql( " is null then 0 else 1 end=0" );
236+
return;
237+
default:
238+
// Fall through
239+
break;
240+
}
241+
}
216242
// HANA does not support plain parameters in the select clause of the intersect emulation
217243
withParameterRenderingMode(
218244
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
219245
() -> renderComparisonEmulateIntersect( lhs, operator, rhs )
220246
);
221247
}
222248
else {
223-
renderComparisonEmulateIntersect( lhs, operator, rhs );
249+
if ( isLob ) {
250+
switch ( operator ) {
251+
case EQUAL:
252+
lhs.accept( this );
253+
appendSql( " like " );
254+
rhs.accept( this );
255+
return;
256+
case NOT_EQUAL:
257+
lhs.accept( this );
258+
appendSql( " not like " );
259+
rhs.accept( this );
260+
return;
261+
default:
262+
// Fall through
263+
break;
264+
}
265+
}
266+
renderComparisonStandard( lhs, operator, rhs );
224267
}
225268
}
226269

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
277277
functionFactory.jsonObjectAgg_h2();
278278
}
279279

280+
functionFactory.unnest( "c1" );
281+
280282
//trim() requires parameters to be cast when used as trim character
281283
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
282284
this,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.hibernate.sql.ast.tree.expression.Literal;
2323
import org.hibernate.sql.ast.tree.expression.SqlTuple;
2424
import org.hibernate.sql.ast.tree.expression.Summarization;
25+
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
26+
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
2527
import org.hibernate.sql.ast.tree.from.NamedTableReference;
2628
import org.hibernate.sql.ast.tree.insert.ConflictClause;
2729
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
@@ -72,6 +74,17 @@ protected void renderDmlTargetTableExpression(NamedTableReference tableReference
7274
}
7375
}
7476

77+
@Override
78+
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
79+
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
80+
// No need for a lateral keyword for functions
81+
tableReference.accept( this );
82+
}
83+
else {
84+
super.renderDerivedTableReference( tableReference );
85+
}
86+
}
87+
7588
@Override
7689
protected void visitConflictClause(ConflictClause conflictClause) {
7790
if ( conflictClause != null ) {

0 commit comments

Comments
 (0)