Skip to content

Commit 1fbb1a9

Browse files
committed
HHH-15328 Add support for CTE WITH clause
1 parent 8d9019e commit 1fbb1a9

File tree

163 files changed

+6170
-1303
lines changed

Some content is hidden

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

163 files changed

+6170
-1303
lines changed

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
3737
renderCombinedLimitClause( queryPart );
3838
}
3939

40-
@Override
41-
protected void renderSearchClause(CteStatement cte) {
42-
// CUBRID does not support this, but it's just a hint anyway
43-
}
44-
45-
@Override
46-
protected void renderCycleClause(CteStatement cte) {
47-
// CUBRID does not support this, but it can be emulated
48-
}
49-
5040
@Override
5141
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
5242
renderComparisonEmulateIntersect( lhs, operator, rhs );

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
8080
}
8181
}
8282

83-
@Override
84-
protected void renderSearchClause(CteStatement cte) {
85-
// Cache does not support this, but it's just a hint anyway
86-
}
87-
88-
@Override
89-
protected void renderCycleClause(CteStatement cte) {
90-
// Cache does not support this, but it can be emulated
91-
}
92-
9383
@Override
9484
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
9585
renderComparisonEmulateIntersect( lhs, operator, rhs );

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ public boolean supportsNonQueryWithCTE() {
348348
return true;
349349
}
350350

351+
@Override
352+
public boolean supportsRecursiveCTE() {
353+
return getVersion().isSameOrAfter( 20, 1 );
354+
}
355+
351356
@Override
352357
public String getNoColumnsInsertString() {
353358
return "default values";

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.hibernate.engine.spi.SessionFactoryImplementor;
1010
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
1111
import org.hibernate.sql.ast.tree.Statement;
12+
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
1213
import org.hibernate.sql.ast.tree.cte.CteStatement;
1314
import org.hibernate.sql.ast.tree.expression.Expression;
1415
import org.hibernate.sql.ast.tree.expression.Literal;
@@ -52,6 +53,26 @@ public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanEx
5253
}
5354
}
5455

56+
@Override
57+
protected void renderMaterializationHint(CteMaterialization materialization) {
58+
if ( getDialect().getVersion().isSameOrAfter( 20, 2 ) ) {
59+
if ( materialization == CteMaterialization.NOT_MATERIALIZED ) {
60+
appendSql( "not " );
61+
}
62+
appendSql( "materialized " );
63+
}
64+
}
65+
66+
@Override
67+
protected boolean supportsRowConstructor() {
68+
return true;
69+
}
70+
71+
@Override
72+
protected boolean supportsArrayConstructor() {
73+
return true;
74+
}
75+
5576
@Override
5677
protected String getForShare(int timeoutMillis) {
5778
return " for share";
@@ -111,16 +132,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
111132
}
112133
}
113134

114-
@Override
115-
protected void renderSearchClause(CteStatement cte) {
116-
// Cockroach does not support this, but it's just a hint anyway
117-
}
118-
119-
@Override
120-
protected void renderCycleClause(CteStatement cte) {
121-
// Cockroach does not support this, but it can be emulated
122-
}
123-
124135
@Override
125136
protected void renderPartitionItem(Expression expression) {
126137
if ( expression instanceof Literal ) {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,11 @@ public boolean supportsLockTimeouts() {
532532
return false;
533533
}
534534

535+
@Override
536+
public boolean requiresCastForConcatenatingNonStrings() {
537+
return true;
538+
}
539+
535540
@Override
536541
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
537542
return selectNullString(sqlType);
@@ -754,6 +759,12 @@ public boolean supportsNonQueryWithCTE() {
754759
return true;
755760
}
756761

762+
@Override
763+
public boolean supportsRecursiveCTE() {
764+
// Supported at last since 9.7
765+
return getDB2Version().isSameOrAfter( 9, 7 );
766+
}
767+
757768
@Override
758769
public boolean supportsOffsetInSubquery() {
759770
return true;

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.List;
1010
import java.util.function.Consumer;
1111

12+
import org.hibernate.LockMode;
1213
import org.hibernate.dialect.DatabaseVersion;
1314
import org.hibernate.engine.spi.SessionFactoryImplementor;
1415
import org.hibernate.query.sqm.ComparisonOperator;
@@ -26,6 +27,9 @@
2627
import org.hibernate.sql.ast.tree.expression.Literal;
2728
import org.hibernate.sql.ast.tree.expression.SqlTuple;
2829
import org.hibernate.sql.ast.tree.expression.Summarization;
30+
import org.hibernate.sql.ast.tree.from.TableGroup;
31+
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
32+
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
2933
import org.hibernate.sql.ast.tree.insert.InsertStatement;
3034
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
3135
import org.hibernate.sql.ast.tree.select.QueryGroup;
@@ -45,6 +49,70 @@ public DB2LegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, State
4549
super( sessionFactory, statement );
4650
}
4751

52+
@Override
53+
protected boolean needsRecursiveKeywordInWithClause() {
54+
return false;
55+
}
56+
57+
@Override
58+
protected boolean supportsWithClauseInSubquery() {
59+
return false;
60+
}
61+
62+
@Override
63+
protected void renderTableReferenceJoins(TableGroup tableGroup) {
64+
// When we are in a recursive CTE, we can't render joins on DB2...
65+
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
66+
if ( isInRecursiveQueryPart() ) {
67+
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
68+
if ( joins == null || joins.isEmpty() ) {
69+
return;
70+
}
71+
72+
for ( TableReferenceJoin tableJoin : joins ) {
73+
switch ( tableJoin.getJoinType() ) {
74+
case CROSS:
75+
case INNER:
76+
break;
77+
default:
78+
throw new UnsupportedOperationException( "Can't emulate '" + tableJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
79+
}
80+
appendSql( COMA_SEPARATOR_CHAR );
81+
82+
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
83+
84+
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
85+
addAdditionalWherePredicate( tableJoin.getPredicate() );
86+
}
87+
}
88+
}
89+
else {
90+
super.renderTableReferenceJoins( tableGroup );
91+
}
92+
}
93+
94+
@Override
95+
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
96+
if ( isInRecursiveQueryPart() ) {
97+
switch ( tableGroupJoin.getJoinType() ) {
98+
case CROSS:
99+
case INNER:
100+
break;
101+
default:
102+
throw new UnsupportedOperationException( "Can't emulate '" + tableGroupJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
103+
}
104+
appendSql( COMA_SEPARATOR_CHAR );
105+
106+
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
107+
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
108+
addAdditionalWherePredicate( tableGroupJoin.getPredicate() );
109+
}
110+
}
111+
else {
112+
super.renderTableGroupJoin( tableGroupJoin, tableGroupJoinCollector );
113+
}
114+
}
115+
48116
@Override
49117
protected void renderExpressionAsClauseItem(Expression expression) {
50118
expression.accept( this );

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ public boolean supportsLateral() {
123123
return getVersion().isSameOrAfter( 7, 1 );
124124
}
125125

126+
@Override
127+
public boolean supportsRecursiveCTE() {
128+
return getVersion().isSameOrAfter( 7, 1 );
129+
}
130+
126131
@Override
127132
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
128133
return new StandardSqlAstTranslatorFactory() {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public boolean supportsLateral() {
125125
return true;
126126
}
127127

128+
@Override
129+
public boolean supportsRecursiveCTE() {
130+
return getVersion().isSameOrAfter( 11 );
131+
}
132+
128133
@Override
129134
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
130135
StringBuilder pattern = new StringBuilder();

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,11 @@ public boolean supportsOrderByInSubquery() {
603603
return getVersion().isSameOrAfter( 10, 5 );
604604
}
605605

606+
@Override
607+
public boolean requiresCastForConcatenatingNonStrings() {
608+
return true;
609+
}
610+
606611
@Override
607612
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
608613
super.contributeTypes( typeContributions, serviceRegistry );

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

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public DerbyLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Sta
4141
super( sessionFactory, statement );
4242
}
4343

44+
@Override
45+
protected boolean supportsWithClause() {
46+
return false;
47+
}
48+
4449
@Override
4550
protected void renderExpressionAsClauseItem(Expression expression) {
4651
expression.accept( this );
@@ -125,24 +130,6 @@ protected String getForUpdateWithClause() {
125130
return " with rs";
126131
}
127132

128-
@Override
129-
public void visitCteContainer(CteContainer cteContainer) {
130-
if ( cteContainer.isWithRecursive() ) {
131-
throw new IllegalArgumentException( "Recursive CTEs can't be emulated" );
132-
}
133-
super.visitCteContainer( cteContainer );
134-
}
135-
136-
@Override
137-
protected void renderSearchClause(CteStatement cte) {
138-
// Derby does not support this, but it's just a hint anyway
139-
}
140-
141-
@Override
142-
protected void renderCycleClause(CteStatement cte) {
143-
// Derby does not support this, but it can be emulated
144-
}
145-
146133
@Override
147134
public void visitOffsetFetchClause(QueryPart queryPart) {
148135
// Derby only supports the OFFSET and FETCH clause with ROWS

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
136136
}
137137
}
138138

139-
@Override
140-
protected void renderSearchClause(CteStatement cte) {
141-
// Firebird does not support this, but it's just a hint anyway
142-
}
143-
144-
@Override
145-
protected void renderCycleClause(CteStatement cte) {
146-
// Firebird does not support this, but it can be emulated
147-
}
148-
149139
@Override
150140
protected boolean supportsSimpleQueryGrouping() {
151141
// Firebird is quite strict i.e. it requires `select .. union all select * from (select ...)`

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,11 @@ public boolean supportsWindowFunctions() {
658658
return getVersion().isSameOrAfter( 1, 4, 200 );
659659
}
660660

661+
@Override
662+
public boolean supportsRecursiveCTE() {
663+
return getVersion().isSameOrAfter( 1, 4, 196 );
664+
}
665+
661666
@Override
662667
public boolean supportsFetchClause(FetchClauseType type) {
663668
return getVersion().isSameOrAfter( 1, 4, 198 );

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

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
1818
import org.hibernate.sql.ast.spi.SqlSelection;
1919
import org.hibernate.sql.ast.tree.Statement;
20+
import org.hibernate.sql.ast.tree.cte.CteContainer;
2021
import org.hibernate.sql.ast.tree.cte.CteStatement;
2122
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
2223
import org.hibernate.sql.ast.tree.expression.Expression;
@@ -46,6 +47,41 @@ public H2LegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statem
4647
super( sessionFactory, statement );
4748
}
4849

50+
@Override
51+
public void visitCteContainer(CteContainer cteContainer) {
52+
// H2 has various bugs in different versions that make it impossible to use CTEs with parameters reliably
53+
withParameterRenderingMode(
54+
SqlAstNodeRenderingMode.INLINE_PARAMETERS,
55+
() -> super.visitCteContainer( cteContainer )
56+
);
57+
}
58+
59+
@Override
60+
protected boolean needsCteInlining() {
61+
// CTEs in H2 are just so buggy, that we can't reliably use them
62+
return true;
63+
}
64+
65+
@Override
66+
protected boolean supportsWithClauseInSubquery() {
67+
return false;
68+
}
69+
70+
@Override
71+
protected boolean supportsRowConstructor() {
72+
return getDialect().getVersion().isSameOrAfter( 2 );
73+
}
74+
75+
@Override
76+
protected boolean supportsArrayConstructor() {
77+
return getDialect().getVersion().isSameOrAfter( 2 );
78+
}
79+
80+
@Override
81+
protected String getArrayContainsFunction() {
82+
return "array_contains";
83+
}
84+
4985
@Override
5086
protected void renderExpressionAsClauseItem(Expression expression) {
5187
expression.accept( this );
@@ -84,16 +120,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
84120
}
85121
}
86122

87-
@Override
88-
protected void renderSearchClause(CteStatement cte) {
89-
// H2 does not support this, but it's just a hint anyway
90-
}
91-
92-
@Override
93-
protected void renderCycleClause(CteStatement cte) {
94-
// H2 does not support this, but it can be emulated
95-
}
96-
97123
@Override
98124
protected void renderSelectTupleComparison(
99125
List<SqlSelection> lhsExpressions,

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,6 @@ public void visitOffsetFetchClause(QueryPart queryPart) {
8686
}
8787
}
8888

89-
@Override
90-
protected void renderSearchClause(CteStatement cte) {
91-
// Informix does not support this, but it's just a hint anyway
92-
}
93-
94-
@Override
95-
protected void renderCycleClause(CteStatement cte) {
96-
// Informix does not support this, but it can be emulated
97-
}
98-
9989
@Override
10090
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
10191
renderComparisonEmulateIntersect( lhs, operator, rhs );

0 commit comments

Comments
 (0)