Skip to content

Commit d446632

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

File tree

134 files changed

+4897
-392
lines changed

Some content is hidden

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

134 files changed

+4897
-392
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

documentation/src/main/asciidoc/introduction/Entities.adoc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ This annotation is completely optional, and so we don't usually use it.
866866
====
867867

868868
On the other hand a reference to an embeddable type is _never_ polymorphic.
869-
One `@Embeddable` class `F` may inherit a second `@Embeddable` class `E`, but an attribute of type `E` will always refer to an instance of that concrete class `E`, never to an instance of `F`.
869+
One `@Embeddable` class `F` may inherit a second `@Embeddable` class `T`, but an attribute of type `T` will always refer to an instance of that concrete class `T`, never to an instance of `F`.
870870

871871
Usually, embeddable types are stored in a "flattened" format.
872872
Their attributes map columns of the table of their parent entity.
@@ -884,15 +884,15 @@ Next we'll discuss a different kind of relationship: a relationship between Java
884884

885885
An _association_ is a relationship between entities.
886886
We usually classify associations based on their _multiplicity_.
887-
If `E` and `F` are both entity classes, then:
887+
If `T` and `F` are both entity classes, then:
888888

889-
- a _one-to-one_ association relates at most one unique instance `E` with at most one unique instance of `F`,
890-
- a _many-to-one_ association relates zero or more instances of `E` with a unique instance of `F`, and
891-
- a _many-to-many_ association relates zero or more instances of `E` with zero or more instance of `F`.
889+
- a _one-to-one_ association relates at most one unique instance `T` with at most one unique instance of `F`,
890+
- a _many-to-one_ association relates zero or more instances of `T` with a unique instance of `F`, and
891+
- a _many-to-many_ association relates zero or more instances of `T` with zero or more instance of `F`.
892892

893893
An association between entity classes may be either:
894894

895-
- _unidirectional_, navigable from `E` to `F` but not from `F` to `E`, or
895+
- _unidirectional_, navigable from `T` to `F` but not from `F` to `T`, or
896896
- _bidirectional_, and navigable in either direction.
897897

898898
In this example data model, we can see the sorts of associations which are possible:

documentation/src/main/asciidoc/introduction/Generator.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,9 +537,9 @@ Optionally, a query method--or a finder method which returns multiple results--m
537537
| Parameter type | Purpose | Example argument
538538

539539
| `Page` | Specifies a page of query results | Page.first(20)
540-
| `Order<? super E>` | Specifies an entity attribute to order by, if `E` is the entity type returned by the query | Order.asc(Book_.title)
540+
| `Order<? super E>` | Specifies an entity attribute to order by, if `T` is the entity type returned by the query | Order.asc(Book_.title)
541541
| `List<Order? super E>` +
542-
(or varargs) | Specifies entity attributes to order by, if `E` is the entity type returned by the query | List.of(Order.asc(Book_.title), Order.asc(Book_.isbn))
542+
(or varargs) | Specifies entity attributes to order by, if `T` is the entity type returned by the query | List.of(Order.asc(Book_.title), Order.asc(Book_.isbn))
543543
| `Order<Object[]>` | Specifies a column to order by, if the query returns a projection list | Order.asc(1)
544544
| `List<Object[]>` +
545545
(or varargs) | Specifies columns to order by, if the query returns a projection list | List.of(Order.asc(1), Order.desc(2))

documentation/src/main/asciidoc/querylanguage/Expressions.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ It's not usually necessary to specify the precision explicitly.
8989

9090
[NOTE]
9191
====
92-
In a literal with an exponent, the `E` is case-insensitive.
92+
In a literal with an exponent, the `T` is case-insensitive.
9393
Similarly, the Java-style postfix is case-insensitive.
9494
====
9595

documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ It's not usually necessary to specify the precision explicitly.
458458

459459
[NOTE]
460460
====
461-
In a literal with an exponent, the `E` is case-insensitive.
461+
In a literal with an exponent, the `T` is case-insensitive.
462462
Similarly, the Java-style postfix is case-insensitive.
463463
====
464464

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,23 +372,23 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser
372372
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
373373
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
374374
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
375-
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
375+
jdbcTypeRegistry.addTypeConstructor( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
376376
}
377377
else {
378378
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
379-
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
379+
jdbcTypeRegistry.addTypeConstructor( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
380380
}
381381
}
382382
else {
383383
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
384384
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
385385
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
386386
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
387-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
387+
jdbcTypeRegistry.addTypeConstructor( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
388388
}
389389
else {
390390
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
391-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
391+
jdbcTypeRegistry.addTypeConstructor( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
392392
}
393393
}
394394
}
@@ -398,11 +398,11 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser
398398
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
399399
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
400400
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
401-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
401+
jdbcTypeRegistry.addTypeConstructor( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
402402
}
403403
else {
404404
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
405-
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
405+
jdbcTypeRegistry.addTypeConstructor( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
406406
}
407407
}
408408

@@ -518,6 +518,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
518518
functionFactory.jsonArrayAppend_postgresql( false );
519519
functionFactory.jsonArrayInsert_postgresql();
520520

521+
functionFactory.unnest();
522+
521523
// Postgres uses # instead of ^ for XOR
522524
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
523525
.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();
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
296296
}
297297
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
298298
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
299-
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonArrayJdbcType.INSTANCE );
299+
jdbcTypeRegistry.addTypeConstructor( H2JsonArrayJdbcTypeConstructor.INSTANCE );
300300
}
301301
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
302302
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
@@ -427,6 +427,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
427427
else {
428428
functionFactory.listagg_groupConcat();
429429
}
430+
431+
// H2 has a function named "unnest", but it only accepts a scalar
432+
// and since H2 has no support for lateral joins, it's completely useless
433+
// functionFactory.unnest();
430434
}
431435

432436
/**

0 commit comments

Comments
 (0)