HHH-15328 Add support for CTE WITH clause
This commit is contained in:
parent
215d411ffa
commit
90a752a0ee
|
@ -89,6 +89,7 @@ Most of the complexity here arises from the interplay of set operators (`union`,
|
||||||
|
|
||||||
We'll describe the various clauses of a query later in this chapter, but to summarize, a query might have:
|
We'll describe the various clauses of a query later in this chapter, but to summarize, a query might have:
|
||||||
|
|
||||||
|
* a `with` clause, specifying <<hql-with-cte,named subqueries>> to be used in the following query,
|
||||||
* a `select` list, specifying a <<hql-select-clause,projection>> (the things to return from the query),
|
* a `select` list, specifying a <<hql-select-clause,projection>> (the things to return from the query),
|
||||||
* a `from` clause and joins, <<hql-from-clause,specifying>> the entities involved in the query, and how they're <<hql-join,related>> to each other,
|
* a `from` clause and joins, <<hql-from-clause,specifying>> the entities involved in the query, and how they're <<hql-join,related>> to each other,
|
||||||
* a `where` clause, specifying a <<hql-where-clause,restriction>>,
|
* a `where` clause, specifying a <<hql-where-clause,restriction>>,
|
||||||
|
@ -1559,6 +1560,16 @@ It may not refer to other roots declared in the same `from` clause.
|
||||||
|
|
||||||
A subquery may also occur in a <<hql-join-derived, join>>, in which case it may be a correlated subquery.
|
A subquery may also occur in a <<hql-join-derived, join>>, in which case it may be a correlated subquery.
|
||||||
|
|
||||||
|
[[hql-from-cte]]
|
||||||
|
==== Common table expressions in `from` clause
|
||||||
|
|
||||||
|
A _Common table expression (CTE)_ is like a derived root with a name. The big difference is,
|
||||||
|
that the name can be referred to multiple times. It must declare an identification variable.
|
||||||
|
|
||||||
|
The CTE name can be used for a `from` clause root or a `join`, similar to entity names.
|
||||||
|
|
||||||
|
Refer to the <<hql-with-cte,with clause>> chapter for details about CTEs.
|
||||||
|
|
||||||
[[hql-join]]
|
[[hql-join]]
|
||||||
=== Declaring joined entities
|
=== Declaring joined entities
|
||||||
|
|
||||||
|
@ -2477,3 +2488,132 @@ This _almost certainly_ isn't the behavior you were hoping for, and in general w
|
||||||
====
|
====
|
||||||
|
|
||||||
In the next chapter we'll see a completely different way to write queries in Hibernate.
|
In the next chapter we'll see a completely different way to write queries in Hibernate.
|
||||||
|
|
||||||
|
[[hql-with-cte]]
|
||||||
|
==== With clause
|
||||||
|
|
||||||
|
The `with` clause allows to specify _common table expressions (CTEs)_ which can be imagined like named subqueries.
|
||||||
|
Every uncorrelated subquery can be factored to a CTE in the `with` clause. The semantics are equivalent.
|
||||||
|
|
||||||
|
The `with` clause offers features beyond naming subqueries though:
|
||||||
|
|
||||||
|
* Specify materialization hints
|
||||||
|
* Recursive querying
|
||||||
|
|
||||||
|
===== Materialization hint
|
||||||
|
|
||||||
|
The materialization hint `MATERIALIZED` or `NOT MATERIALIZED` can be applied to tell the DBMS whether a CTE should
|
||||||
|
or shouldn't be materialized. Consult the database manual of the respective database for the exact meaning of the hint.
|
||||||
|
|
||||||
|
Usually, one can expect that `MATERIALIZED` will cause the subquery to be executed separately and saved into a temporary table,
|
||||||
|
whereas `NOT MATERIALIZED` will cause the subquery to be inlined into every use site and considered during optimizations separately.
|
||||||
|
|
||||||
|
[[hql-cte-materialized-example]]
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/HQLTest.java[tags=hql-cte-materialized-example, indent=0]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
===== Recursive querying
|
||||||
|
|
||||||
|
The main use case for the `with` clause is to define a name for a subquery,
|
||||||
|
such that this subquery can refer to itself, which ultimately enables recursive querying.
|
||||||
|
|
||||||
|
Recursive CTEs must follow a very particular shape, which is
|
||||||
|
|
||||||
|
* Base query part
|
||||||
|
* `union` or `union all`
|
||||||
|
* Recursive query part
|
||||||
|
|
||||||
|
[[hql-cte-recursive-example]]
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/HQLTest.java[tags=hql-cte-recursive-example, indent=0]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
The base query part represents the initial set of rows. When fetching a tree of data,
|
||||||
|
the base query part usually is the tree root.
|
||||||
|
|
||||||
|
The recursive query part is executed again and again until it produces no new rows.
|
||||||
|
The result of such a CTE is the base query part result _unioned_ together with all recursive query part executions.
|
||||||
|
Depending on whether `union all` or `union` (`distinct`) is used, duplicate rows are preserved or not.
|
||||||
|
|
||||||
|
Recursive queries additionally can have
|
||||||
|
|
||||||
|
* a `search` clause to hint the DBMS whether to use breadth or depth first searching
|
||||||
|
* a `cycle` clause to hint the DBMS how to determine that a cycle was reached
|
||||||
|
|
||||||
|
Defining the `search` clause requires specifying a name for an attribute in the `set` sub-clause,
|
||||||
|
that will be added to the CTE type and allows ordering results according to the search order.
|
||||||
|
|
||||||
|
[[hql-cte-recursive-search-bnf-example]]
|
||||||
|
====
|
||||||
|
[source, antlrv4, indent=0]
|
||||||
|
----
|
||||||
|
searchClause
|
||||||
|
: "SEARCH" ("BREADTH"|"DEPTH") "FIRST BY" searchSpecifications "SET" identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecifications
|
||||||
|
: searchSpecification ("," searchSpecification)*
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecification
|
||||||
|
: identifier sortDirection? nullsPrecedence?
|
||||||
|
;
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
A DBMS has two possible orders when executing the recursive query part
|
||||||
|
|
||||||
|
* Depth first - handle the *newest* produced rows by the recursive query part first
|
||||||
|
* Breadth first - handle the *oldest* produced rows by the recursive query part first
|
||||||
|
|
||||||
|
[[hql-cte-recursive-search-example]]
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/HQLTest.java[tags=hql-cte-recursive-search-example, indent=0]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Recursive processing can lead to cycles which might lead to queries executing forever.
|
||||||
|
The `cycle` clause hints the DBMS which CTE attributes to track for the cycle detection.
|
||||||
|
It requires specifying a name for a cycle mark attribute in the `set` sub-clause,
|
||||||
|
that will be added to the CTE type and allows detecting if a cycle occurred for a result.
|
||||||
|
|
||||||
|
By default, the cycle mark attribute will be set to `true` when a cycle is detected and `false` otherwise.
|
||||||
|
The values to use can be explicitly specified through the `to` and `default` sub-clauses.
|
||||||
|
Optionally, it's also possible to specify a cycle path attribute name through the `using` clause
|
||||||
|
The cycle path attribute can be used to understand the traversal path that lead to a result.
|
||||||
|
|
||||||
|
[[hql-cte-recursive-cycle-bnf-example]]
|
||||||
|
====
|
||||||
|
[source, antlrv4, indent=0]
|
||||||
|
----
|
||||||
|
cycleClause
|
||||||
|
: "CYCLE" cteAttributes "SET" identifier ("TO" literal "DEFAULT" literal)? ("USING" identifier)?
|
||||||
|
;
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[[hql-cte-recursive-cycle-example]]
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/HQLTest.java[tags=hql-cte-recursive-cycle-example, indent=0]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
====
|
||||||
|
Hibernate merely translates recursive CTEs but doesn't attempt to emulate the feature.
|
||||||
|
Therefore, this feature will only work if the database supports recursive CTEs.
|
||||||
|
Hibernate does emulate the `search` and `cycle` clauses though if necessary, so you can safely use that.
|
||||||
|
|
||||||
|
Note that most modern database versions support recursive CTEs already.
|
||||||
|
====
|
|
@ -2,7 +2,7 @@ selectStatement
|
||||||
: queryExpression
|
: queryExpression
|
||||||
|
|
||||||
queryExpression
|
queryExpression
|
||||||
: orderedQuery (setOperator orderedQuery)*
|
: withClause? orderedQuery (setOperator orderedQuery)*
|
||||||
|
|
||||||
orderedQuery
|
orderedQuery
|
||||||
: (query | "(" queryExpression ")") queryOrder?
|
: (query | "(" queryExpression ")") queryOrder?
|
||||||
|
@ -29,4 +29,32 @@ join
|
||||||
|
|
||||||
joinTarget
|
joinTarget
|
||||||
: path variable?
|
: path variable?
|
||||||
| "LATERAL"? "(" subquery ")" variable?
|
| "LATERAL"? "(" subquery ")" variable?
|
||||||
|
|
||||||
|
withClause
|
||||||
|
: "WITH" cte ("," cte)*
|
||||||
|
;
|
||||||
|
|
||||||
|
cte
|
||||||
|
: identifier AS ("NOT"? "MATERIALIZED")? "(" queryExpression ")" searchClause? cycleClause?
|
||||||
|
;
|
||||||
|
|
||||||
|
cteAttributes
|
||||||
|
: identifier ("," identifier)*
|
||||||
|
;
|
||||||
|
|
||||||
|
searchClause
|
||||||
|
: "SEARCH" ("BREADTH"|"DEPTH") "FIRST BY" searchSpecifications "SET" identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecifications
|
||||||
|
: searchSpecification ("," searchSpecification)*
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecification
|
||||||
|
: identifier sortDirection? nullsPrecedence?
|
||||||
|
;
|
||||||
|
|
||||||
|
cycleClause
|
||||||
|
: "CYCLE" cteAttributes "SET" identifier ("TO" literal "DEFAULT" literal)? ("USING" identifier)?
|
||||||
|
;
|
||||||
|
|
|
@ -3133,6 +3133,102 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_hql_cte_materialized_example() {
|
||||||
|
|
||||||
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::hql-cte-materialized-example[]
|
||||||
|
List<Tuple> calls = entityManager.createQuery(
|
||||||
|
"with data as materialized(" +
|
||||||
|
" select p.person as owner, c.payment is not null as payed " +
|
||||||
|
" from Call c " +
|
||||||
|
" join c.phone p " +
|
||||||
|
" where p.number = :phoneNumber" +
|
||||||
|
")" +
|
||||||
|
"select d.owner, d.payed " +
|
||||||
|
"from data d",
|
||||||
|
Tuple.class)
|
||||||
|
.setParameter("phoneNumber", "123-456-7890")
|
||||||
|
.getResultList();
|
||||||
|
//end::hql-cte-materialized-example[]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature( DialectChecks.SupportsRecursiveCtes.class )
|
||||||
|
public void test_hql_cte_recursive_example() {
|
||||||
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::hql-cte-recursive-example[]
|
||||||
|
List<Tuple> calls = entityManager.createQuery(
|
||||||
|
"with paymentConnectedPersons as(" +
|
||||||
|
" select a.owner owner " +
|
||||||
|
" from Account a where a.id = :startId " +
|
||||||
|
" union all" +
|
||||||
|
" select a2.owner owner " +
|
||||||
|
" from paymentConnectedPersons d " +
|
||||||
|
" join Account a on a.owner = d.owner " +
|
||||||
|
" join a.payments p " +
|
||||||
|
" join Account a2 on a2.owner = p.person" +
|
||||||
|
")" +
|
||||||
|
"select d.owner " +
|
||||||
|
"from paymentConnectedPersons d",
|
||||||
|
Tuple.class)
|
||||||
|
.setParameter("startId", 123L)
|
||||||
|
.getResultList();
|
||||||
|
//end::hql-cte-recursive-example[]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature( DialectChecks.SupportsRecursiveCtes.class )
|
||||||
|
public void test_hql_cte_recursive_search_example() {
|
||||||
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::hql-cte-recursive-search-example[]
|
||||||
|
List<Tuple> calls = entityManager.createQuery(
|
||||||
|
"with paymentConnectedPersons as(" +
|
||||||
|
" select a.owner owner " +
|
||||||
|
" from Account a where a.id = :startId " +
|
||||||
|
" union all" +
|
||||||
|
" select a2.owner owner " +
|
||||||
|
" from paymentConnectedPersons d " +
|
||||||
|
" join Account a on a.owner = d.owner " +
|
||||||
|
" join a.payments p " +
|
||||||
|
" join Account a2 on a2.owner = p.person" +
|
||||||
|
") search breadth first by owner set orderAttr " +
|
||||||
|
"select d.owner " +
|
||||||
|
"from paymentConnectedPersons d",
|
||||||
|
Tuple.class)
|
||||||
|
.setParameter("startId", 123L)
|
||||||
|
.getResultList();
|
||||||
|
//end::hql-cte-recursive-search-example[]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature( DialectChecks.SupportsRecursiveCtes.class )
|
||||||
|
public void test_hql_cte_recursive_cycle_example() {
|
||||||
|
doInJPA(this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::hql-cte-recursive-cycle-example[]
|
||||||
|
List<Tuple> calls = entityManager.createQuery(
|
||||||
|
"with paymentConnectedPersons as(" +
|
||||||
|
" select a.owner owner " +
|
||||||
|
" from Account a where a.id = :startId " +
|
||||||
|
" union all" +
|
||||||
|
" select a2.owner owner " +
|
||||||
|
" from paymentConnectedPersons d " +
|
||||||
|
" join Account a on a.owner = d.owner " +
|
||||||
|
" join a.payments p " +
|
||||||
|
" join Account a2 on a2.owner = p.person" +
|
||||||
|
") cycle owner set cycleMark " +
|
||||||
|
"select d.owner, d.cycleMark " +
|
||||||
|
"from paymentConnectedPersons d",
|
||||||
|
Tuple.class)
|
||||||
|
.setParameter("startId", 123L)
|
||||||
|
.getResultList();
|
||||||
|
//end::hql-cte-recursive-cycle-example[]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature({
|
@RequiresDialectFeature({
|
||||||
DialectChecks.SupportsSubqueryInOnClause.class,
|
DialectChecks.SupportsSubqueryInOnClause.class,
|
||||||
|
|
|
@ -155,6 +155,14 @@ ext {
|
||||||
'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test',
|
'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test',
|
||||||
'connection.init_sql' : ''
|
'connection.init_sql' : ''
|
||||||
],
|
],
|
||||||
|
tidb_ci5 : [
|
||||||
|
'db.dialect' : 'org.hibernate.dialect.TiDBDialect',
|
||||||
|
'jdbc.driver': 'com.mysql.jdbc.Driver',
|
||||||
|
'jdbc.user' : 'root',
|
||||||
|
'jdbc.pass' : '',
|
||||||
|
'jdbc.url' : 'jdbc:mysql://' + dbHost + ':4000/test',
|
||||||
|
'connection.init_sql' : ''
|
||||||
|
],
|
||||||
postgis : [
|
postgis : [
|
||||||
'db.dialect' : 'org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect',
|
'db.dialect' : 'org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect',
|
||||||
'jdbc.driver': 'org.postgresql.Driver',
|
'jdbc.driver': 'org.postgresql.Driver',
|
||||||
|
|
|
@ -37,16 +37,6 @@ public class CUBRIDSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
renderCombinedLimitClause( queryPart );
|
renderCombinedLimitClause( queryPart );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// CUBRID does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// CUBRID does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -80,16 +80,6 @@ public class CacheSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Cache does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Cache does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -468,6 +468,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 20, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNoColumnsInsertString() {
|
public String getNoColumnsInsertString() {
|
||||||
return "default values";
|
return "default values";
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.community.dialect;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
@ -52,6 +53,26 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderMaterializationHint(CteMaterialization materialization) {
|
||||||
|
if ( getDialect().getVersion().isSameOrAfter( 20, 2 ) ) {
|
||||||
|
if ( materialization == CteMaterialization.NOT_MATERIALIZED ) {
|
||||||
|
appendSql( "not " );
|
||||||
|
}
|
||||||
|
appendSql( "materialized " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getForShare(int timeoutMillis) {
|
protected String getForShare(int timeoutMillis) {
|
||||||
return " for share";
|
return " for share";
|
||||||
|
@ -116,16 +137,6 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Cockroach does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Cockroach does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderPartitionItem(Expression expression) {
|
protected void renderPartitionItem(Expression expression) {
|
||||||
if ( expression instanceof Literal ) {
|
if ( expression instanceof Literal ) {
|
||||||
|
|
|
@ -534,6 +534,11 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
||||||
return selectNullString(sqlType);
|
return selectNullString(sqlType);
|
||||||
|
@ -756,6 +761,12 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
// Supported at last since 9.7
|
||||||
|
return getDB2Version().isSameOrAfter( 9, 7 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsOffsetInSubquery() {
|
public boolean supportsOffsetInSubquery() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.community.dialect;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
@ -26,6 +27,9 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||||
|
@ -45,6 +49,70 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
||||||
|
// When we are in a recursive CTE, we can't render joins on DB2...
|
||||||
|
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
||||||
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
|
||||||
|
if ( joins == null || joins.isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( TableReferenceJoin tableJoin : joins ) {
|
||||||
|
switch ( tableJoin.getJoinType() ) {
|
||||||
|
case CROSS:
|
||||||
|
case INNER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException( "Can't emulate '" + tableJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
|
||||||
|
}
|
||||||
|
appendSql( COMA_SEPARATOR_CHAR );
|
||||||
|
|
||||||
|
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
|
||||||
|
|
||||||
|
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
|
||||||
|
addAdditionalWherePredicate( tableJoin.getPredicate() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderTableReferenceJoins( tableGroup );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||||
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
switch ( tableGroupJoin.getJoinType() ) {
|
||||||
|
case CROSS:
|
||||||
|
case INNER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException( "Can't emulate '" + tableGroupJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
|
||||||
|
}
|
||||||
|
appendSql( COMA_SEPARATOR_CHAR );
|
||||||
|
|
||||||
|
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
|
||||||
|
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
|
||||||
|
addAdditionalWherePredicate( tableGroupJoin.getPredicate() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderTableGroupJoin( tableGroupJoin, tableGroupJoinCollector );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
|
|
@ -123,6 +123,11 @@ public class DB2iLegacyDialect extends DB2LegacyDialect {
|
||||||
return getVersion().isSameOrAfter( 7, 1 );
|
return getVersion().isSameOrAfter( 7, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 7, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
return new StandardSqlAstTranslatorFactory() {
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
|
|
@ -125,6 +125,11 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 11 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
StringBuilder pattern = new StringBuilder();
|
StringBuilder pattern = new StringBuilder();
|
||||||
|
|
|
@ -61,6 +61,10 @@ public class DB2zLegacySqlAstTranslator<T extends JdbcOperation> extends DB2Lega
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
if ( shouldInlineCte( tableGroup ) ) {
|
||||||
|
inlineCteTableGroup( tableGroup, lockMode );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.dialect.NationalizationSupport;
|
||||||
import org.hibernate.dialect.RowLockStrategy;
|
import org.hibernate.dialect.RowLockStrategy;
|
||||||
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
|
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
|
||||||
import org.hibernate.dialect.function.CastingConcatFunction;
|
import org.hibernate.dialect.function.CastingConcatFunction;
|
||||||
|
import org.hibernate.dialect.function.ChrLiteralEmulation;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.function.CountFunction;
|
import org.hibernate.dialect.function.CountFunction;
|
||||||
import org.hibernate.dialect.function.DerbyLpadEmulation;
|
import org.hibernate.dialect.function.DerbyLpadEmulation;
|
||||||
|
@ -286,6 +287,16 @@ public class DerbyLegacyDialect extends Dialect {
|
||||||
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
// 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 );
|
functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT );
|
||||||
|
|
||||||
|
// Note that Derby does not have chr() / ascii() functions.
|
||||||
|
// It does have a function named char(), but it's really a
|
||||||
|
// sort of to_char() function.
|
||||||
|
|
||||||
|
// We register an emulation instead, that can at least translate integer literals
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"chr",
|
||||||
|
new ChrLiteralEmulation( queryEngine.getTypeConfiguration() )
|
||||||
|
);
|
||||||
|
|
||||||
functionFactory.concat_pipeOperator();
|
functionFactory.concat_pipeOperator();
|
||||||
functionFactory.cot();
|
functionFactory.cot();
|
||||||
functionFactory.chr_char();
|
functionFactory.chr_char();
|
||||||
|
@ -604,6 +615,11 @@ public class DerbyLegacyDialect extends Dialect {
|
||||||
return getVersion().isSameOrAfter( 10, 5 );
|
return getVersion().isSameOrAfter( 10, 5 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
super.contributeTypes( typeContributions, serviceRegistry );
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
|
|
|
@ -41,6 +41,11 @@ public class DerbyLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
@ -125,24 +130,6 @@ public class DerbyLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
return " with rs";
|
return " with rs";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCteContainer(CteContainer cteContainer) {
|
|
||||||
if ( cteContainer.isWithRecursive() ) {
|
|
||||||
throw new IllegalArgumentException( "Recursive CTEs can't be emulated" );
|
|
||||||
}
|
|
||||||
super.visitCteContainer( cteContainer );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Derby does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Derby does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
// Derby only supports the OFFSET and FETCH clause with ROWS
|
// Derby only supports the OFFSET and FETCH clause with ROWS
|
||||||
|
|
|
@ -136,16 +136,6 @@ public class FirebirdSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Firebird does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Firebird does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean supportsSimpleQueryGrouping() {
|
protected boolean supportsSimpleQueryGrouping() {
|
||||||
// Firebird is quite strict i.e. it requires `select .. union all select * from (select ...)`
|
// Firebird is quite strict i.e. it requires `select .. union all select * from (select ...)`
|
||||||
|
|
|
@ -768,6 +768,11 @@ public class H2LegacyDialect extends Dialect {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 200 );
|
return getVersion().isSameOrAfter( 1, 4, 200 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 1, 4, 196 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFetchClause(FetchClauseType type) {
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
|
|
|
@ -17,7 +17,9 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
@ -46,6 +48,47 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCteContainer(CteContainer cteContainer) {
|
||||||
|
// H2 has various bugs in different versions that make it impossible to use CTEs with parameters reliably
|
||||||
|
withParameterRenderingMode(
|
||||||
|
SqlAstNodeRenderingMode.INLINE_PARAMETERS,
|
||||||
|
() -> super.visitCteContainer( cteContainer )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsCteInlining() {
|
||||||
|
// CTEs in H2 are just so buggy, that we can't reliably use them
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldInlineCte(TableGroup tableGroup) {
|
||||||
|
return tableGroup instanceof CteTableGroup
|
||||||
|
&& !getCteStatement( tableGroup.getPrimaryTableReference().getTableId() ).isRecursive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getArrayContainsFunction() {
|
||||||
|
return "array_contains";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
@ -84,16 +127,6 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// H2 does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// H2 does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSelectTupleComparison(
|
protected void renderSelectTupleComparison(
|
||||||
List<SqlSelection> lhsExpressions,
|
List<SqlSelection> lhsExpressions,
|
||||||
|
|
|
@ -59,6 +59,39 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
// Doesn't support correlations in the WITH clause
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveClauseArrayAndRowEmulation() {
|
||||||
|
// Even though HSQL supports the array constructor, it's illegal to use arrays in CTEs
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// HSQL determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as varchar(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HSQL does not allow CASE expressions where all result arms contain plain parameters.
|
// HSQL does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -190,16 +223,6 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// HSQL does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// HSQL does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSelectExpression(Expression expression) {
|
protected void renderSelectExpression(Expression expression) {
|
||||||
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
|
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
|
||||||
|
|
|
@ -86,16 +86,6 @@ public class InformixSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Informix does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Informix does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -93,16 +93,6 @@ public class IngresSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Ingres does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Ingres does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -50,7 +50,8 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MariaDBLegacyDialect(DialectResolutionInfo info) {
|
public MariaDBLegacyDialect(DialectResolutionInfo info) {
|
||||||
super(info);
|
super( createVersion( info ), getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() ) );
|
||||||
|
registerKeywords( info );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,6 +102,17 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
||||||
return getVersion().isSameOrAfter( 10, 2 );
|
return getVersion().isSameOrAfter( 10, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
// See https://jira.mariadb.org/browse/MDEV-19078
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColumnCheck() {
|
public boolean supportsColumnCheck() {
|
||||||
return getVersion().isSameOrAfter( 10, 2 );
|
return getVersion().isSameOrAfter( 10, 2 );
|
||||||
|
|
|
@ -32,11 +32,37 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// MariaDB determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as char(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
@ -91,16 +117,6 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MariaDB does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MariaDB does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonDistinctOperator( lhs, operator, rhs );
|
renderComparisonDistinctOperator( lhs, operator, rhs );
|
||||||
|
|
|
@ -38,16 +38,6 @@ public class MaxDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
renderLimitOffsetClause( queryPart );
|
renderLimitOffsetClause( queryPart );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MaxDB does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MaxDB does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -38,16 +38,6 @@ public class MimerSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
renderOffsetFetchClause( queryPart, true );
|
renderOffsetFetchClause( queryPart, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MimerSQL does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MimerSQL does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -123,17 +123,35 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MySQLLegacyDialect(DatabaseVersion version) {
|
public MySQLLegacyDialect(DatabaseVersion version) {
|
||||||
|
this( version, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MySQLLegacyDialect(DatabaseVersion version, int bytesPerCharacter) {
|
||||||
super( version );
|
super( version );
|
||||||
registerKeyword( "key" );
|
maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter ); //conservative assumption
|
||||||
maxVarcharLength = maxVarcharLength( getMySQLVersion(), 4 ); //conservative assumption
|
|
||||||
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MySQLLegacyDialect(DialectResolutionInfo info) {
|
public MySQLLegacyDialect(DialectResolutionInfo info) {
|
||||||
super( info );
|
this( createVersion( info ), getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() ) );
|
||||||
int bytesPerCharacter = getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() );
|
registerKeywords( info );
|
||||||
maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter );
|
}
|
||||||
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
|
||||||
|
protected static DatabaseVersion createVersion(DialectResolutionInfo info) {
|
||||||
|
final String versionString = info.getDatabaseVersion();
|
||||||
|
final String[] components = versionString.split( "\\." );
|
||||||
|
if ( components.length >= 3 ) {
|
||||||
|
try {
|
||||||
|
final int majorVersion = Integer.parseInt( components[0] );
|
||||||
|
final int minorVersion = Integer.parseInt( components[1] );
|
||||||
|
final int patchLevel = Integer.parseInt( components[2] );
|
||||||
|
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ex) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info.makeCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -518,9 +536,9 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
// MySQL timestamp type defaults to precision 0 (seconds) but
|
// MySQL timestamp type defaults to precision 0 (seconds) but
|
||||||
// we want the standard default precision of 6 (microseconds)
|
// we want the standard default precision of 6 (microseconds)
|
||||||
functionFactory.sysdateExplicitMicros();
|
functionFactory.sysdateExplicitMicros();
|
||||||
if ( getMySQLVersion().isSameOrAfter( 8, 2 ) ) {
|
if ( getMySQLVersion().isSameOrAfter( 8, 0, 2 ) ) {
|
||||||
functionFactory.windowFunctions();
|
functionFactory.windowFunctions();
|
||||||
if ( getMySQLVersion().isSameOrAfter( 8, 11 ) ) {
|
if ( getMySQLVersion().isSameOrAfter( 8, 0, 11 ) ) {
|
||||||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1211,12 +1229,17 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsWindowFunctions() {
|
public boolean supportsWindowFunctions() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8, 2 );
|
return getMySQLVersion().isSameOrAfter( 8, 0, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8, 14 );
|
return getMySQLVersion().isSameOrAfter( 8, 0, 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getMySQLVersion().isSameOrAfter( 8, 0, 14 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1240,6 +1263,12 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
return supportsAliasLocks() ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
return supportsAliasLocks() ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerDefaultKeywords() {
|
||||||
|
super.registerDefaultKeywords();
|
||||||
|
registerKeyword( "key" );
|
||||||
|
}
|
||||||
|
|
||||||
boolean supportsForShare() {
|
boolean supportsForShare() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8 );
|
return getMySQLVersion().isSameOrAfter( 8 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
@ -38,6 +37,22 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// MySQL determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as char(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
@ -103,16 +118,6 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MySQL does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MySQL does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonDistinctOperator( lhs, operator, rhs );
|
renderComparisonDistinctOperator( lhs, operator, rhs );
|
||||||
|
@ -160,6 +165,11 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 8 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getFromDual() {
|
protected String getFromDual() {
|
||||||
return " from dual";
|
return " from dual";
|
||||||
|
|
|
@ -1122,6 +1122,11 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 11, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getVersion().isSameOrAfter( 12, 1 );
|
return getVersion().isSameOrAfter( 12, 1 );
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -54,6 +55,37 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
// Oracle has some limitations, see ORA-32034, so we just report false here for simplicity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveSearchClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveCycleClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlSelection(SqlSelection sqlSelection) {
|
||||||
|
if ( getCurrentCteStatement() != null ) {
|
||||||
|
if ( getCurrentCteStatement().getMaterialization() == CteMaterialization.MATERIALIZED ) {
|
||||||
|
appendSql( "/*+ materialize */ " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitSqlSelection( sqlSelection );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LockStrategy determineLockingStrategy(
|
protected LockStrategy determineLockingStrategy(
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
|
@ -169,31 +201,20 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
true, // we need select aliases to avoid ORA-00918: column ambiguously defined
|
true, // we need select aliases to avoid ORA-00918: column ambiguously defined
|
||||||
() -> {
|
() -> {
|
||||||
final QueryPart currentQueryPart = getQueryPartStack().getCurrent();
|
final QueryPart currentQueryPart = getQueryPartStack().getCurrent();
|
||||||
final boolean needsParenthesis;
|
|
||||||
final boolean needsWrapper;
|
final boolean needsWrapper;
|
||||||
if ( currentQueryPart instanceof QueryGroup ) {
|
if ( currentQueryPart instanceof QueryGroup ) {
|
||||||
needsParenthesis = false;
|
|
||||||
// visitQuerySpec will add the select wrapper
|
// visitQuerySpec will add the select wrapper
|
||||||
needsWrapper = !currentQueryPart.hasOffsetOrFetchClause();
|
needsWrapper = !currentQueryPart.hasOffsetOrFetchClause();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
needsParenthesis = !querySpec.isRoot();
|
|
||||||
needsWrapper = true;
|
needsWrapper = true;
|
||||||
}
|
}
|
||||||
if ( needsWrapper ) {
|
if ( needsWrapper ) {
|
||||||
if ( needsParenthesis ) {
|
appendSql( "select * from (" );
|
||||||
appendSql( '(' );
|
|
||||||
}
|
|
||||||
appendSql( "select * from " );
|
|
||||||
if ( !needsParenthesis ) {
|
|
||||||
appendSql( '(' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
super.visitQuerySpec( querySpec );
|
super.visitQuerySpec( querySpec );
|
||||||
if ( needsWrapper ) {
|
if ( needsWrapper ) {
|
||||||
if ( !needsParenthesis ) {
|
appendSql( ')' );
|
||||||
appendSql( ')' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
appendSql( " where rownum<=" );
|
appendSql( " where rownum<=" );
|
||||||
final Stack<Clause> clauseStack = getClauseStack();
|
final Stack<Clause> clauseStack = getClauseStack();
|
||||||
|
@ -209,12 +230,6 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
finally {
|
finally {
|
||||||
clauseStack.pop();
|
clauseStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( needsWrapper ) {
|
|
||||||
if ( needsParenthesis ) {
|
|
||||||
appendSql( ')' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,16 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFilterClause() {
|
public boolean supportsFilterClause() {
|
||||||
return getDialect().getVersion().isSameOrAfter( 9, 4 );
|
return getDialect().getVersion().isSameOrAfter( 9, 4 );
|
||||||
|
@ -117,13 +127,27 @@ public class PostgreSQLLegacySqlAstTranslator<T extends JdbcOperation> extends A
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
protected boolean supportsRecursiveSearchClause() {
|
||||||
// PostgreSQL does not support this, but it's just a hint anyway
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
protected boolean supportsRecursiveCycleClause() {
|
||||||
// PostgreSQL does not support this, but it can be emulated
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveCycleUsingClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderStandardCycleClause(CteStatement cte) {
|
||||||
|
super.renderStandardCycleClause( cte );
|
||||||
|
if ( cte.getCycleMarkColumn() != null && cte.getCyclePathColumn() == null && supportsRecursiveCycleUsingClause() ) {
|
||||||
|
appendSql( " using " );
|
||||||
|
appendSql( determineCyclePathColumnName( cte ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -77,16 +77,6 @@ public class RDMSOS2200SqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Unisys 2200 does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Unisys 2200 does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -614,6 +614,11 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
return getVersion().isSameOrAfter( 9 );
|
return getVersion().isSameOrAfter( 9 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 9 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFetchClause(FetchClauseType type) {
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
return getVersion().isSameOrAfter( 11 );
|
return getVersion().isSameOrAfter( 11 );
|
||||||
|
|
|
@ -52,6 +52,16 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||||
appendSql( WHITESPACE );
|
appendSql( WHITESPACE );
|
||||||
|
@ -87,6 +97,10 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
if ( shouldInlineCte( tableGroup ) ) {
|
||||||
|
inlineCteTableGroup( tableGroup, lockMode );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
@ -377,16 +391,6 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// SQL Server does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// SQL Server does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -105,16 +105,6 @@ public class SQLiteSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// SQLite does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// SQLite does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
if ( rhs instanceof Any ) {
|
if ( rhs instanceof Any ) {
|
||||||
|
|
|
@ -49,6 +49,11 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
|
// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -152,16 +157,6 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
super.renderForUpdateClause( querySpec, forUpdateClause );
|
super.renderForUpdateClause( querySpec, forUpdateClause );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Sybase ASE does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Sybase ASE does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void visitSqlSelections(SelectClause selectClause) {
|
protected void visitSqlSelections(SelectClause selectClause) {
|
||||||
if ( supportsTopClause() ) {
|
if ( supportsTopClause() ) {
|
||||||
|
|
|
@ -157,16 +157,6 @@ public class SybaseAnywhereSqlAstTranslator<T extends JdbcOperation> extends Abs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Sybase Anywhere does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Sybase Anywhere does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Sybase does not allow CASE expressions where all result arms contain plain parameters.
|
// Sybase does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -105,16 +110,6 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
// Sybase does not support the FOR UPDATE clause
|
// Sybase does not support the FOR UPDATE clause
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Sybase does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Sybase does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
assertRowsOnlyFetchClauseType( queryPart );
|
assertRowsOnlyFetchClauseType( queryPart );
|
||||||
|
|
|
@ -101,16 +101,6 @@ public class TeradataSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Teradata does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Teradata does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -89,16 +89,6 @@ public class TimesTenSqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// TimesTen does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// TimesTen does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void visitSqlSelections(SelectClause selectClause) {
|
protected void visitSqlSelections(SelectClause selectClause) {
|
||||||
renderRowsToClause( (QuerySpec) getQueryPartStack().getCurrent() );
|
renderRowsToClause( (QuerySpec) getQueryPartStack().getCurrent() );
|
||||||
|
|
|
@ -149,6 +149,7 @@ ASC : [aA] [sS] [cC];
|
||||||
AVG : [aA] [vV] [gG];
|
AVG : [aA] [vV] [gG];
|
||||||
BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN];
|
BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN];
|
||||||
BOTH : [bB] [oO] [tT] [hH];
|
BOTH : [bB] [oO] [tT] [hH];
|
||||||
|
BREADTH : [bB] [rR] [eE] [aA] [dD] [tT] [hH];
|
||||||
BY : [bB] [yY];
|
BY : [bB] [yY];
|
||||||
CASE : [cC] [aA] [sS] [eE];
|
CASE : [cC] [aA] [sS] [eE];
|
||||||
CAST : [cC] [aA] [sS] [tT];
|
CAST : [cC] [aA] [sS] [tT];
|
||||||
|
@ -161,10 +162,13 @@ CURRENT_DATE : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE];
|
||||||
CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT]; //deprecated legacy
|
CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT]; //deprecated legacy
|
||||||
CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE];
|
CURRENT_TIME : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE];
|
||||||
CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
|
CURRENT_TIMESTAMP : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
|
||||||
|
CYCLE : [cC] [yY] [cC] [lL] [eE];
|
||||||
DATE : [dD] [aA] [tT] [eE];
|
DATE : [dD] [aA] [tT] [eE];
|
||||||
DATETIME : [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
DATETIME : [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
||||||
DAY : [dD] [aA] [yY];
|
DAY : [dD] [aA] [yY];
|
||||||
|
DEFAULT : [dD] [eE] [fF] [aA] [uU] [lL] [tT];
|
||||||
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
|
||||||
|
DEPTH : [dD] [eE] [pP] [tT] [hH];
|
||||||
DESC : [dD] [eE] [sS] [cC];
|
DESC : [dD] [eE] [sS] [cC];
|
||||||
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
|
||||||
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
||||||
|
@ -219,6 +223,7 @@ LOCAL_DATE : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE];
|
||||||
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
|
||||||
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
|
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
|
||||||
MAP : [mM] [aA] [pP];
|
MAP : [mM] [aA] [pP];
|
||||||
|
MATERIALIZED : [mM] [aA] [tT] [eE] [rR] [iI] [aA] [lL] [iI] [zZ] [eE] [dD];
|
||||||
MAX : [mM] [aA] [xX];
|
MAX : [mM] [aA] [xX];
|
||||||
MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
|
||||||
MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX];
|
MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX];
|
||||||
|
@ -262,6 +267,7 @@ RIGHT : [rR] [iI] [gG] [hH] [tT];
|
||||||
ROLLUP : [rR] [oO] [lL] [lL] [uU] [pP];
|
ROLLUP : [rR] [oO] [lL] [lL] [uU] [pP];
|
||||||
ROW : [rR] [oO] [wW];
|
ROW : [rR] [oO] [wW];
|
||||||
ROWS : [rR] [oO] [wW] [sS];
|
ROWS : [rR] [oO] [wW] [sS];
|
||||||
|
SEARCH : [sS] [eE] [aA] [rR] [cC] [hH];
|
||||||
SECOND : [sS] [eE] [cC] [oO] [nN] [dD];
|
SECOND : [sS] [eE] [cC] [oO] [nN] [dD];
|
||||||
SELECT : [sS] [eE] [lL] [eE] [cC] [tT];
|
SELECT : [sS] [eE] [lL] [eE] [cC] [tT];
|
||||||
SET : [sS] [eE] [tT];
|
SET : [sS] [eE] [tT];
|
||||||
|
@ -275,6 +281,7 @@ TIME : [tT] [iI] [mM] [eE];
|
||||||
TIMESTAMP : [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
|
TIMESTAMP : [tT] [iI] [mM] [eE] [sS] [tT] [aA] [mM] [pP];
|
||||||
TIMEZONE_HOUR : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [hH] [oO] [uU] [rR];
|
TIMEZONE_HOUR : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [hH] [oO] [uU] [rR];
|
||||||
TIMEZONE_MINUTE : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [mM] [iI] [nN] [uU] [tT] [eE];
|
TIMEZONE_MINUTE : [tT] [iI] [mM] [eE] [zZ] [oO] [nN] [eE] '_' [mM] [iI] [nN] [uU] [tT] [eE];
|
||||||
|
TO : [tT] [oO];
|
||||||
TRAILING : [tT] [rR] [aA] [iI] [lL] [iI] [nN] [gG];
|
TRAILING : [tT] [rR] [aA] [iI] [lL] [iI] [nN] [gG];
|
||||||
TREAT : [tT] [rR] [eE] [aA] [tT];
|
TREAT : [tT] [rR] [eE] [aA] [tT];
|
||||||
TRIM : [tT] [rR] [iI] [mM];
|
TRIM : [tT] [rR] [iI] [mM];
|
||||||
|
@ -283,6 +290,7 @@ TYPE : [tT] [yY] [pP] [eE];
|
||||||
UNBOUNDED : [uU] [nN] [bB] [oO] [uU] [nN] [dD] [eE] [dD];
|
UNBOUNDED : [uU] [nN] [bB] [oO] [uU] [nN] [dD] [eE] [dD];
|
||||||
UNION : [uU] [nN] [iI] [oO] [nN];
|
UNION : [uU] [nN] [iI] [oO] [nN];
|
||||||
UPDATE : [uU] [pP] [dD] [aA] [tT] [eE];
|
UPDATE : [uU] [pP] [dD] [aA] [tT] [eE];
|
||||||
|
USING : [uU] [sS] [iI] [nN] [gG];
|
||||||
VALUE : [vV] [aA] [lL] [uU] [eE];
|
VALUE : [vV] [aA] [lL] [uU] [eE];
|
||||||
VALUES : [vV] [aA] [lL] [uU] [eE] [sS];
|
VALUES : [vV] [aA] [lL] [uU] [eE] [sS];
|
||||||
WEEK : [wW] [eE] [eE] [kK];
|
WEEK : [wW] [eE] [eE] [kK];
|
||||||
|
|
|
@ -110,12 +110,40 @@ values
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// QUERY SPEC - general structure of root sqm or sub sqm
|
// QUERY SPEC - general structure of root sqm or sub sqm
|
||||||
|
|
||||||
|
withClause
|
||||||
|
: WITH cte (COMMA cte)*
|
||||||
|
;
|
||||||
|
|
||||||
|
cte
|
||||||
|
: identifier AS (NOT? MATERIALIZED)? LEFT_PAREN queryExpression RIGHT_PAREN searchClause? cycleClause?
|
||||||
|
;
|
||||||
|
|
||||||
|
cteAttributes
|
||||||
|
: identifier (COMMA identifier)*
|
||||||
|
;
|
||||||
|
|
||||||
|
searchClause
|
||||||
|
: SEARCH (BREADTH|DEPTH) FIRST BY searchSpecifications SET identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecifications
|
||||||
|
: searchSpecification (COMMA searchSpecification)*
|
||||||
|
;
|
||||||
|
|
||||||
|
searchSpecification
|
||||||
|
: identifier sortDirection? nullsPrecedence?
|
||||||
|
;
|
||||||
|
|
||||||
|
cycleClause
|
||||||
|
: CYCLE cteAttributes SET identifier (TO literal DEFAULT literal)? (USING identifier)?
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A toplevel query of subquery, which may be a union or intersection of subqueries
|
* A toplevel query of subquery, which may be a union or intersection of subqueries
|
||||||
*/
|
*/
|
||||||
queryExpression
|
queryExpression
|
||||||
: orderedQuery # SimpleQueryGroup
|
: withClause? orderedQuery # SimpleQueryGroup
|
||||||
| orderedQuery (setOperator orderedQuery)+ # SetQueryGroup
|
| withClause? orderedQuery (setOperator orderedQuery)+ # SetQueryGroup
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1482,6 +1510,7 @@ rollup
|
||||||
| AVG
|
| AVG
|
||||||
| BETWEEN
|
| BETWEEN
|
||||||
| BOTH
|
| BOTH
|
||||||
|
| BREADTH
|
||||||
| BY
|
| BY
|
||||||
| CASE
|
| CASE
|
||||||
| CAST
|
| CAST
|
||||||
|
@ -1494,10 +1523,13 @@ rollup
|
||||||
| CURRENT_INSTANT
|
| CURRENT_INSTANT
|
||||||
| CURRENT_TIME
|
| CURRENT_TIME
|
||||||
| CURRENT_TIMESTAMP
|
| CURRENT_TIMESTAMP
|
||||||
|
| CYCLE
|
||||||
| DATE
|
| DATE
|
||||||
| DATETIME
|
| DATETIME
|
||||||
| DAY
|
| DAY
|
||||||
|
| DEFAULT
|
||||||
| DELETE
|
| DELETE
|
||||||
|
| DEPTH
|
||||||
| DESC
|
| DESC
|
||||||
| DISTINCT
|
| DISTINCT
|
||||||
| ELEMENT
|
| ELEMENT
|
||||||
|
@ -1552,6 +1584,7 @@ rollup
|
||||||
| LOCAL_DATETIME
|
| LOCAL_DATETIME
|
||||||
| LOCAL_TIME
|
| LOCAL_TIME
|
||||||
| MAP
|
| MAP
|
||||||
|
| MATERIALIZED
|
||||||
| MAX
|
| MAX
|
||||||
| MAXELEMENT
|
| MAXELEMENT
|
||||||
| MAXINDEX
|
| MAXINDEX
|
||||||
|
@ -1596,6 +1629,7 @@ rollup
|
||||||
| ROLLUP
|
| ROLLUP
|
||||||
| ROW
|
| ROW
|
||||||
| ROWS
|
| ROWS
|
||||||
|
| SEARCH
|
||||||
| SECOND
|
| SECOND
|
||||||
| SELECT
|
| SELECT
|
||||||
| SET
|
| SET
|
||||||
|
@ -1609,6 +1643,7 @@ rollup
|
||||||
| TIMESTAMP
|
| TIMESTAMP
|
||||||
| TIMEZONE_HOUR
|
| TIMEZONE_HOUR
|
||||||
| TIMEZONE_MINUTE
|
| TIMEZONE_MINUTE
|
||||||
|
| TO
|
||||||
| TRAILING
|
| TRAILING
|
||||||
| TREAT
|
| TREAT
|
||||||
| TRIM
|
| TRIM
|
||||||
|
@ -1617,6 +1652,7 @@ rollup
|
||||||
| UNBOUNDED
|
| UNBOUNDED
|
||||||
| UNION
|
| UNION
|
||||||
| UPDATE
|
| UPDATE
|
||||||
|
| USING
|
||||||
| VALUE
|
| VALUE
|
||||||
| VALUES
|
| VALUES
|
||||||
| VERSION
|
| VERSION
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.sqm.tree.SqmDmlStatement;
|
import org.hibernate.query.sqm.tree.SqmDmlStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmQuery;
|
||||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||||
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
|
@ -151,8 +152,8 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
|
||||||
entityPersisters.add( metamodel.getEntityDescriptor( statement.getTarget().getEntityName() ) );
|
entityPersisters.add( metamodel.getEntityDescriptor( statement.getTarget().getEntityName() ) );
|
||||||
}
|
}
|
||||||
for ( SqmCteStatement<?> cteStatement : statement.getCteStatements() ) {
|
for ( SqmCteStatement<?> cteStatement : statement.getCteStatements() ) {
|
||||||
final SqmStatement<?> cteDefinition = cteStatement.getCteDefinition();
|
final SqmQuery<?> cteDefinition = cteStatement.getCteDefinition();
|
||||||
if ( cteDefinition instanceof SqmDmlStatement<?> && !( cteDefinition instanceof InsertStatement ) ) {
|
if ( cteDefinition instanceof SqmDmlStatement<?> ) {
|
||||||
entityPersisters.add(
|
entityPersisters.add(
|
||||||
metamodel.getEntityDescriptor( ( (SqmDmlStatement<?>) cteDefinition ).getTarget().getEntityName() )
|
metamodel.getEntityDescriptor( ( (SqmDmlStatement<?>) cteDefinition ).getTarget().getEntityName() )
|
||||||
);
|
);
|
||||||
|
|
|
@ -290,6 +290,11 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
|
||||||
return NullOrdering.SMALLEST;
|
return NullOrdering.SMALLEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
|
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
|
|
|
@ -474,6 +474,11 @@ public class CockroachDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNoColumnsInsertString() {
|
public String getNoColumnsInsertString() {
|
||||||
return "default values";
|
return "default values";
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.dialect;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
@ -52,6 +53,24 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderMaterializationHint(CteMaterialization materialization) {
|
||||||
|
if ( materialization == CteMaterialization.NOT_MATERIALIZED ) {
|
||||||
|
appendSql( "not " );
|
||||||
|
}
|
||||||
|
appendSql( "materialized " );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getForShare(int timeoutMillis) {
|
protected String getForShare(int timeoutMillis) {
|
||||||
return " for share";
|
return " for share";
|
||||||
|
@ -90,16 +109,6 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Cockroach does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Cockroach does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderPartitionItem(Expression expression) {
|
protected void renderPartitionItem(Expression expression) {
|
||||||
if ( expression instanceof Literal ) {
|
if ( expression instanceof Literal ) {
|
||||||
|
|
|
@ -507,6 +507,11 @@ public class DB2Dialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
||||||
return selectNullString(sqlType);
|
return selectNullString(sqlType);
|
||||||
|
@ -729,6 +734,12 @@ public class DB2Dialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
// Supported at last since 9.7
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsOffsetInSubquery() {
|
public boolean supportsOffsetInSubquery() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.dialect;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
|
@ -25,6 +26,9 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||||
|
@ -44,6 +48,70 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderTableReferenceJoins(TableGroup tableGroup) {
|
||||||
|
// When we are in a recursive CTE, we can't render joins on DB2...
|
||||||
|
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
|
||||||
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
final List<TableReferenceJoin> joins = tableGroup.getTableReferenceJoins();
|
||||||
|
if ( joins == null || joins.isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( TableReferenceJoin tableJoin : joins ) {
|
||||||
|
switch ( tableJoin.getJoinType() ) {
|
||||||
|
case CROSS:
|
||||||
|
case INNER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException( "Can't emulate '" + tableJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
|
||||||
|
}
|
||||||
|
appendSql( COMA_SEPARATOR_CHAR );
|
||||||
|
|
||||||
|
renderNamedTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE );
|
||||||
|
|
||||||
|
if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) {
|
||||||
|
addAdditionalWherePredicate( tableJoin.getPredicate() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderTableReferenceJoins( tableGroup );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||||
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
switch ( tableGroupJoin.getJoinType() ) {
|
||||||
|
case CROSS:
|
||||||
|
case INNER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException( "Can't emulate '" + tableGroupJoin.getJoinType().getText() + "join' in a DB2 recursive query part" );
|
||||||
|
}
|
||||||
|
appendSql( COMA_SEPARATOR_CHAR );
|
||||||
|
|
||||||
|
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
|
||||||
|
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
|
||||||
|
addAdditionalWherePredicate( tableGroupJoin.getPredicate() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderTableGroupJoin( tableGroupJoin, tableGroupJoinCollector );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
|
|
@ -128,6 +128,11 @@ public class DB2iDialect extends DB2Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
return new StandardSqlAstTranslatorFactory() {
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
|
|
@ -123,6 +123,11 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
StringBuilder pattern = new StringBuilder();
|
StringBuilder pattern = new StringBuilder();
|
||||||
|
|
|
@ -54,6 +54,10 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
if ( shouldInlineCte( tableGroup ) ) {
|
||||||
|
inlineCteTableGroup( tableGroup, lockMode );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.dialect;
|
||||||
|
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.function.CastingConcatFunction;
|
import org.hibernate.dialect.function.CastingConcatFunction;
|
||||||
|
import org.hibernate.dialect.function.ChrLiteralEmulation;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.function.CountFunction;
|
import org.hibernate.dialect.function.CountFunction;
|
||||||
import org.hibernate.dialect.function.DerbyLpadEmulation;
|
import org.hibernate.dialect.function.DerbyLpadEmulation;
|
||||||
|
@ -256,6 +257,12 @@ public class DerbyDialect extends Dialect {
|
||||||
// It does have a function named char(), but it's really a
|
// It does have a function named char(), but it's really a
|
||||||
// sort of to_char() function.
|
// sort of to_char() function.
|
||||||
|
|
||||||
|
// We register an emulation instead, that can at least translate integer literals
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"chr",
|
||||||
|
new ChrLiteralEmulation( queryEngine.getTypeConfiguration() )
|
||||||
|
);
|
||||||
|
|
||||||
functionFactory.concat_pipeOperator();
|
functionFactory.concat_pipeOperator();
|
||||||
functionFactory.cot();
|
functionFactory.cot();
|
||||||
functionFactory.degrees();
|
functionFactory.degrees();
|
||||||
|
@ -562,6 +569,11 @@ public class DerbyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
super.contributeTypes( typeContributions, serviceRegistry );
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
|
|
|
@ -41,6 +41,11 @@ public class DerbySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
@ -125,24 +130,6 @@ public class DerbySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
return " with rs";
|
return " with rs";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCteContainer(CteContainer cteContainer) {
|
|
||||||
if ( cteContainer.isWithRecursive() ) {
|
|
||||||
throw new IllegalArgumentException( "Recursive CTEs can't be emulated" );
|
|
||||||
}
|
|
||||||
super.visitCteContainer( cteContainer );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Derby does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Derby does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
// Derby only supports the OFFSET and FETCH clause with ROWS
|
// Derby only supports the OFFSET and FETCH clause with ROWS
|
||||||
|
|
|
@ -2991,6 +2991,16 @@ public abstract class Dialect implements ConversionContext {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this dialect/database require casting of non-string arguments in a concat function?
|
||||||
|
*
|
||||||
|
* @return {@code true} if casting of non-string arguments in concat is required
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public boolean requiresCastForConcatenatingNonStrings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this dialect require that integer divisions be wrapped in {@code cast()}
|
* Does this dialect require that integer divisions be wrapped in {@code cast()}
|
||||||
* calls to tell the db parser the expected type.
|
* calls to tell the db parser the expected type.
|
||||||
|
@ -3574,6 +3584,16 @@ public abstract class Dialect implements ConversionContext {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this dialect/database support recursive CTEs (Common Table Expressions)?
|
||||||
|
*
|
||||||
|
* @return {@code true} if recursive CTEs are supported
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this dialect support {@code values} lists of form
|
* Does this dialect support {@code values} lists of form
|
||||||
* {@code VALUES (1), (2), (3)}?
|
* {@code VALUES (1), (2), (3)}?
|
||||||
|
|
|
@ -735,6 +735,11 @@ public class H2Dialect extends Dialect {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 200 );
|
return getVersion().isSameOrAfter( 1, 4, 200 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 1, 4, 196 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFetchClause(FetchClauseType type) {
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
return getVersion().isSameOrAfter( 1, 4, 198 );
|
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
|
|
|
@ -17,7 +17,9 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
@ -46,6 +48,47 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCteContainer(CteContainer cteContainer) {
|
||||||
|
// H2 has various bugs in different versions that make it impossible to use CTEs with parameters reliably
|
||||||
|
withParameterRenderingMode(
|
||||||
|
SqlAstNodeRenderingMode.INLINE_PARAMETERS,
|
||||||
|
() -> super.visitCteContainer( cteContainer )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsCteInlining() {
|
||||||
|
// CTEs in H2 are just so buggy, that we can't reliably use them
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldInlineCte(TableGroup tableGroup) {
|
||||||
|
return tableGroup instanceof CteTableGroup
|
||||||
|
&& !getCteStatement( tableGroup.getPrimaryTableReference().getTableId() ).isRecursive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getArrayContainsFunction() {
|
||||||
|
return "array_contains";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
|
@ -84,16 +127,6 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// H2 does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// H2 does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSelectTupleComparison(
|
protected void renderSelectTupleComparison(
|
||||||
List<SqlSelection> lhsExpressions,
|
List<SqlSelection> lhsExpressions,
|
||||||
|
|
|
@ -44,6 +44,18 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
&& !isRowsOnlyFetchClauseType( queryPart );
|
&& !isRowsOnlyFetchClauseType( queryPart );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
// HANA doesn't seem to support correlation, so we just report false here for simplicity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCorrelated(CteStatement cteStatement) {
|
||||||
|
// Report false here, because apparently HANA does not need the "lateral" keyword to correlate a from clause subquery in a subquery
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitQueryGroup(QueryGroup queryGroup) {
|
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||||
if ( shouldEmulateFetchClause( queryGroup ) ) {
|
if ( shouldEmulateFetchClause( queryGroup ) ) {
|
||||||
|
@ -95,16 +107,6 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// HANA does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// HANA does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -632,6 +632,11 @@ public class HSQLDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requiresFloatCastingOfIntegerDivision() {
|
public boolean requiresFloatCastingOfIntegerDivision() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,7 +17,6 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||||
|
@ -58,6 +57,39 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
// Doesn't support correlations in the WITH clause
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveClauseArrayAndRowEmulation() {
|
||||||
|
// Even though HSQL supports the array constructor, it's illegal to use arrays in CTEs
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// HSQL determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as varchar(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HSQL does not allow CASE expressions where all result arms contain plain parameters.
|
// HSQL does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -170,16 +202,6 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// HSQL does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// HSQL does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSelectExpression(Expression expression) {
|
protected void renderSelectExpression(Expression expression) {
|
||||||
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
|
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
|
||||||
|
|
|
@ -56,7 +56,8 @@ public class MariaDBDialect extends MySQLDialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MariaDBDialect(DialectResolutionInfo info) {
|
public MariaDBDialect(DialectResolutionInfo info) {
|
||||||
super(info);
|
super( createVersion( info ), getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() ) );
|
||||||
|
registerKeywords( info );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -149,6 +150,17 @@ public class MariaDBDialect extends MySQLDialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
// See https://jira.mariadb.org/browse/MDEV-19078
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColumnCheck() {
|
public boolean supportsColumnCheck() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -32,11 +32,32 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderExpressionAsClauseItem(Expression expression) {
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// MariaDB determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as char(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
@ -91,16 +112,6 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MariaDB does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MariaDB does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonDistinctOperator( lhs, operator, rhs );
|
renderComparisonDistinctOperator( lhs, operator, rhs );
|
||||||
|
|
|
@ -119,17 +119,37 @@ public class MySQLDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MySQLDialect(DatabaseVersion version) {
|
public MySQLDialect(DatabaseVersion version) {
|
||||||
|
this( version, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MySQLDialect(DatabaseVersion version, int bytesPerCharacter) {
|
||||||
super( version );
|
super( version );
|
||||||
registerKeyword( "key" );
|
maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter ); //conservative assumption
|
||||||
maxVarcharLength = maxVarcharLength( getMySQLVersion(), 4 ); //conservative assumption
|
|
||||||
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MySQLDialect(DialectResolutionInfo info) {
|
public MySQLDialect(DialectResolutionInfo info) {
|
||||||
super( info );
|
this( createVersion( info ), getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() ) );
|
||||||
int bytesPerCharacter = getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() );
|
registerKeywords( info );
|
||||||
maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter );
|
}
|
||||||
maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() );
|
|
||||||
|
protected static DatabaseVersion createVersion(DialectResolutionInfo info) {
|
||||||
|
final String versionString = info.getDatabaseVersion();
|
||||||
|
if ( versionString != null ) {
|
||||||
|
final String[] components = versionString.split( "\\." );
|
||||||
|
if ( components.length >= 3 ) {
|
||||||
|
try {
|
||||||
|
final int majorVersion = Integer.parseInt( components[0] );
|
||||||
|
final int minorVersion = Integer.parseInt( components[1] );
|
||||||
|
final int patchLevel = Integer.parseInt( components[2] );
|
||||||
|
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ex) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info.makeCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -532,9 +552,9 @@ public class MySQLDialect extends Dialect {
|
||||||
// MySQL timestamp type defaults to precision 0 (seconds) but
|
// MySQL timestamp type defaults to precision 0 (seconds) but
|
||||||
// we want the standard default precision of 6 (microseconds)
|
// we want the standard default precision of 6 (microseconds)
|
||||||
functionFactory.sysdateExplicitMicros();
|
functionFactory.sysdateExplicitMicros();
|
||||||
if ( getMySQLVersion().isSameOrAfter( 8, 2 ) ) {
|
if ( getMySQLVersion().isSameOrAfter( 8, 0, 2 ) ) {
|
||||||
functionFactory.windowFunctions();
|
functionFactory.windowFunctions();
|
||||||
if ( getMySQLVersion().isSameOrAfter( 8, 11 ) ) {
|
if ( getMySQLVersion().isSameOrAfter( 8, 0, 11 ) ) {
|
||||||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1219,12 +1239,17 @@ public class MySQLDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsWindowFunctions() {
|
public boolean supportsWindowFunctions() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8, 2 );
|
return getMySQLVersion().isSameOrAfter( 8, 0, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8, 14 );
|
return getMySQLVersion().isSameOrAfter( 8, 0, 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return getMySQLVersion().isSameOrAfter( 8, 0, 14 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1248,6 +1273,12 @@ public class MySQLDialect extends Dialect {
|
||||||
return supportsAliasLocks() ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
return supportsAliasLocks() ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerDefaultKeywords() {
|
||||||
|
super.registerDefaultKeywords();
|
||||||
|
registerKeyword( "key" );
|
||||||
|
}
|
||||||
|
|
||||||
boolean supportsForShare() {
|
boolean supportsForShare() {
|
||||||
return getMySQLVersion().isSameOrAfter( 8 );
|
return getMySQLVersion().isSameOrAfter( 8 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
@ -38,6 +37,22 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
expression.accept( this );
|
expression.accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitRecursivePath(Expression recursivePath, int sizeEstimate) {
|
||||||
|
// MySQL determines the type and size of a column in a recursive CTE based on the expression of the non-recursive part
|
||||||
|
// Due to that, we have to cast the path in the non-recursive path to a varchar of appropriate size to avoid data truncation errors
|
||||||
|
if ( sizeEstimate == -1 ) {
|
||||||
|
super.visitRecursivePath( recursivePath, sizeEstimate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "cast(" );
|
||||||
|
recursivePath.accept( this );
|
||||||
|
appendSql( " as char(" );
|
||||||
|
appendSql( sizeEstimate );
|
||||||
|
appendSql( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
@ -103,16 +118,6 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// MySQL does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// MySQL does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonDistinctOperator( lhs, operator, rhs );
|
renderComparisonDistinctOperator( lhs, operator, rhs );
|
||||||
|
@ -170,6 +175,11 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 8 );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getFromDual() {
|
protected String getFromDual() {
|
||||||
return " from dual";
|
return " from dual";
|
||||||
|
|
|
@ -1073,6 +1073,11 @@ public class OracleDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getVersion().isSameOrAfter( 12, 1 );
|
return getVersion().isSameOrAfter( 12, 1 );
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -54,6 +55,37 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
// Oracle has some limitations, see ORA-32034, so we just report false here for simplicity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveSearchClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveCycleClause() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlSelection(SqlSelection sqlSelection) {
|
||||||
|
if ( getCurrentCteStatement() != null ) {
|
||||||
|
if ( getCurrentCteStatement().getMaterialization() == CteMaterialization.MATERIALIZED ) {
|
||||||
|
appendSql( "/*+ materialize */ " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitSqlSelection( sqlSelection );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LockStrategy determineLockingStrategy(
|
protected LockStrategy determineLockingStrategy(
|
||||||
QuerySpec querySpec,
|
QuerySpec querySpec,
|
||||||
|
@ -169,31 +201,20 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
true, // we need select aliases to avoid ORA-00918: column ambiguously defined
|
true, // we need select aliases to avoid ORA-00918: column ambiguously defined
|
||||||
() -> {
|
() -> {
|
||||||
final QueryPart currentQueryPart = getQueryPartStack().getCurrent();
|
final QueryPart currentQueryPart = getQueryPartStack().getCurrent();
|
||||||
final boolean needsParenthesis;
|
|
||||||
final boolean needsWrapper;
|
final boolean needsWrapper;
|
||||||
if ( currentQueryPart instanceof QueryGroup ) {
|
if ( currentQueryPart instanceof QueryGroup ) {
|
||||||
needsParenthesis = false;
|
|
||||||
// visitQuerySpec will add the select wrapper
|
// visitQuerySpec will add the select wrapper
|
||||||
needsWrapper = !currentQueryPart.hasOffsetOrFetchClause();
|
needsWrapper = !currentQueryPart.hasOffsetOrFetchClause();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
needsParenthesis = !querySpec.isRoot();
|
|
||||||
needsWrapper = true;
|
needsWrapper = true;
|
||||||
}
|
}
|
||||||
if ( needsWrapper ) {
|
if ( needsWrapper ) {
|
||||||
if ( needsParenthesis ) {
|
appendSql( "select * from (" );
|
||||||
appendSql( '(' );
|
|
||||||
}
|
|
||||||
appendSql( "select * from " );
|
|
||||||
if ( !needsParenthesis ) {
|
|
||||||
appendSql( '(' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
super.visitQuerySpec( querySpec );
|
super.visitQuerySpec( querySpec );
|
||||||
if ( needsWrapper ) {
|
if ( needsWrapper ) {
|
||||||
if ( !needsParenthesis ) {
|
appendSql( ')' );
|
||||||
appendSql( ')' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
appendSql( " where rownum<=" );
|
appendSql( " where rownum<=" );
|
||||||
final Stack<Clause> clauseStack = getClauseStack();
|
final Stack<Clause> clauseStack = getClauseStack();
|
||||||
|
@ -209,12 +230,6 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
finally {
|
finally {
|
||||||
clauseStack.pop();
|
clauseStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( needsWrapper ) {
|
|
||||||
if ( needsParenthesis ) {
|
|
||||||
appendSql( ')' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1184,6 +1184,11 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFetchClause(FetchClauseType type) {
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
switch ( type ) {
|
switch ( type ) {
|
||||||
|
|
|
@ -60,6 +60,16 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsArrayConstructor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFilterClause() {
|
public boolean supportsFilterClause() {
|
||||||
return getDialect().getVersion().isSameOrAfter( 9, 4 );
|
return getDialect().getVersion().isSameOrAfter( 9, 4 );
|
||||||
|
@ -117,13 +127,27 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
protected boolean supportsRecursiveSearchClause() {
|
||||||
// PostgreSQL does not support this, but it's just a hint anyway
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
protected boolean supportsRecursiveCycleClause() {
|
||||||
// PostgreSQL does not support this, but it can be emulated
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRecursiveCycleUsingClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 14 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderStandardCycleClause(CteStatement cte) {
|
||||||
|
super.renderStandardCycleClause( cte );
|
||||||
|
if ( cte.getCycleMarkColumn() != null && cte.getCyclePathColumn() == null && supportsRecursiveCycleUsingClause() ) {
|
||||||
|
appendSql( " using " );
|
||||||
|
appendSql( determineCyclePathColumnName( cte ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -614,6 +614,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFetchClause(FetchClauseType type) {
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
return getVersion().isSameOrAfter( 11 );
|
return getVersion().isSameOrAfter( 11 );
|
||||||
|
|
|
@ -51,6 +51,16 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean needsRecursiveKeywordInWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClauseInSubquery() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||||
appendSql( WHITESPACE );
|
appendSql( WHITESPACE );
|
||||||
|
@ -86,6 +96,10 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
if ( shouldInlineCte( tableGroup ) ) {
|
||||||
|
inlineCteTableGroup( tableGroup, lockMode );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
@ -353,16 +367,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// SQL Server does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// SQL Server does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
|
|
@ -61,16 +61,6 @@ public class SpannerSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
renderLimitOffsetClause( queryPart );
|
renderLimitOffsetClause( queryPart );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Spanner does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Spanner does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
|
@ -121,6 +111,10 @@ public class SpannerSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
if ( shouldInlineCte( tableGroup ) ) {
|
||||||
|
inlineCteTableGroup( tableGroup, lockMode );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
|
|
@ -49,6 +49,11 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
|
// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -138,16 +143,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Sybase ASE does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Sybase ASE does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void visitSqlSelections(SelectClause selectClause) {
|
protected void visitSqlSelections(SelectClause selectClause) {
|
||||||
if ( supportsTopClause() ) {
|
if ( supportsTopClause() ) {
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
super( sessionFactory, statement );
|
super( sessionFactory, statement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsWithClause() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Sybase does not allow CASE expressions where all result arms contain plain parameters.
|
// Sybase does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
// At least one result arm must provide some type context for inference,
|
// At least one result arm must provide some type context for inference,
|
||||||
// so we cast the first result arm if we encounter this condition
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
@ -105,16 +110,6 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
// Sybase does not support the FOR UPDATE clause
|
// Sybase does not support the FOR UPDATE clause
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// Sybase does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// Sybase does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
assertRowsOnlyFetchClauseType( queryPart );
|
assertRowsOnlyFetchClauseType( queryPart );
|
||||||
|
|
|
@ -39,7 +39,8 @@ public class TiDBDialect extends MySQLDialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TiDBDialect(DialectResolutionInfo info) {
|
public TiDBDialect(DialectResolutionInfo info) {
|
||||||
super(info);
|
super( createVersion( info ), getCharacterSetBytesPerCharacter( info.getDatabaseMetadata() ) );
|
||||||
|
registerKeywords( info );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,6 +99,11 @@ public class TiDBDialect extends MySQLDialect {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRecursiveCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsNoWait() {
|
public boolean supportsNoWait() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -95,16 +95,6 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderSearchClause(CteStatement cte) {
|
|
||||||
// TiDB does not support this, but it's just a hint anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderCycleClause(CteStatement cte) {
|
|
||||||
// TiDB does not support this, but it can be emulated
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
renderComparisonDistinctOperator( lhs, operator, rhs );
|
renderComparisonDistinctOperator( lhs, operator, rhs );
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
import org.hibernate.sql.results.internal.ResolvedSqlSelection;
|
import org.hibernate.sql.results.internal.ResolvedSqlSelection;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
@ -417,7 +418,7 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme
|
||||||
final QueryPartTableGroup queryPartTableGroup = new QueryPartTableGroup(
|
final QueryPartTableGroup queryPartTableGroup = new QueryPartTableGroup(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
null,
|
null,
|
||||||
subQuerySpec,
|
new SelectStatement( subQuerySpec ),
|
||||||
identifierVariable,
|
identifierVariable,
|
||||||
columnNames,
|
columnNames,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.hibernate.QueryException;
|
||||||
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
|
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chr implementation that translates integer literals to string literals.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class ChrLiteralEmulation extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
|
|
||||||
|
public ChrLiteralEmulation(TypeConfiguration typeConfiguration) {
|
||||||
|
super(
|
||||||
|
"chr",
|
||||||
|
new ArgumentTypesValidator(
|
||||||
|
StandardArgumentsValidators.composite(
|
||||||
|
StandardArgumentsValidators.exactly( 1 ),
|
||||||
|
(arguments, functionName, queryEngine) -> {
|
||||||
|
if ( !( arguments.get( 0 ) instanceof SqmLiteral<?> ) ) {
|
||||||
|
throw new QueryException(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"Emulation of function chr() supports only integer literals, but %s argument given",
|
||||||
|
arguments.get( 0 ).getClass().getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
INTEGER
|
||||||
|
),
|
||||||
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
|
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.CHARACTER )
|
||||||
|
),
|
||||||
|
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, INTEGER )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
List<? extends SqlAstNode> arguments,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final QueryLiteral<Number> literal = (QueryLiteral<Number>) arguments.get( 0 );
|
||||||
|
sqlAppender.appendSql( '\'' );
|
||||||
|
sqlAppender.appendSql( (char) literal.getLiteralValue().intValue() );
|
||||||
|
sqlAppender.appendSql( '\'' );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.expression.Distinct;
|
import org.hibernate.sql.ast.tree.expression.Distinct;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
import org.hibernate.sql.ast.tree.expression.Star;
|
import org.hibernate.sql.ast.tree.expression.Star;
|
||||||
|
@ -54,7 +55,14 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
TypeConfiguration typeConfiguration,
|
TypeConfiguration typeConfiguration,
|
||||||
SqlAstNodeRenderingMode defaultArgumentRenderingMode,
|
SqlAstNodeRenderingMode defaultArgumentRenderingMode,
|
||||||
String concatOperator) {
|
String concatOperator) {
|
||||||
this( dialect, typeConfiguration, defaultArgumentRenderingMode, concatOperator, null, false );
|
this(
|
||||||
|
dialect,
|
||||||
|
typeConfiguration,
|
||||||
|
defaultArgumentRenderingMode,
|
||||||
|
concatOperator,
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CountFunction(
|
public CountFunction(
|
||||||
|
@ -142,6 +150,19 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
// '' -> \0 + argumentNumber
|
// '' -> \0 + argumentNumber
|
||||||
// In the end, the expression looks like the following:
|
// In the end, the expression looks like the following:
|
||||||
// count(distinct coalesce(nullif(coalesce(col1 || '', '\0'), ''), '\01') || '\0' || coalesce(nullif(coalesce(col2 || '', '\0'), ''), '\02'))
|
// count(distinct coalesce(nullif(coalesce(col1 || '', '\0'), ''), '\01') || '\0' || coalesce(nullif(coalesce(col2 || '', '\0'), ''), '\02'))
|
||||||
|
final AbstractSqmSelfRenderingFunctionDescriptor chr =
|
||||||
|
(AbstractSqmSelfRenderingFunctionDescriptor) translator.getSessionFactory()
|
||||||
|
.getQueryEngine()
|
||||||
|
.getSqmFunctionRegistry()
|
||||||
|
.findFunctionDescriptor( "chr" );
|
||||||
|
final List<Expression> chrArguments = List.of(
|
||||||
|
new QueryLiteral<>(
|
||||||
|
0,
|
||||||
|
translator.getSessionFactory()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getBasicTypeForJavaType( Integer.class )
|
||||||
|
)
|
||||||
|
);
|
||||||
if ( caseWrapper ) {
|
if ( caseWrapper ) {
|
||||||
translator.getCurrentClauseStack().push( Clause.WHERE );
|
translator.getCurrentClauseStack().push( Clause.WHERE );
|
||||||
sqlAppender.appendSql( "case when " );
|
sqlAppender.appendSql( "case when " );
|
||||||
|
@ -161,11 +182,16 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
sqlAppender.appendSql( concatOperator );
|
sqlAppender.appendSql( concatOperator );
|
||||||
sqlAppender.appendSql( "''" );
|
sqlAppender.appendSql( "''" );
|
||||||
}
|
}
|
||||||
sqlAppender.appendSql( ",'\\0'),''),'\\0" );
|
sqlAppender.appendSql( SqlAppender.COMA_SEPARATOR_CHAR );
|
||||||
|
chr.render( sqlAppender, chrArguments, translator );
|
||||||
|
sqlAppender.appendSql( "),'')," );
|
||||||
|
chr.render( sqlAppender, chrArguments, translator );
|
||||||
|
sqlAppender.appendSql( concatOperator );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
sqlAppender.appendSql( argumentNumber );
|
sqlAppender.appendSql( argumentNumber );
|
||||||
sqlAppender.appendSql( "')" );
|
sqlAppender.appendSql( "')" );
|
||||||
sqlAppender.appendSql( concatOperator );
|
sqlAppender.appendSql( concatOperator );
|
||||||
sqlAppender.appendSql( "'\\0'" );
|
chr.render( sqlAppender, chrArguments, translator );
|
||||||
sqlAppender.appendSql( concatOperator );
|
sqlAppender.appendSql( concatOperator );
|
||||||
sqlAppender.appendSql( "coalesce(nullif(coalesce(" );
|
sqlAppender.appendSql( "coalesce(nullif(coalesce(" );
|
||||||
needsConcat = renderCastedArgument( sqlAppender, translator, expressions.get( i ) );
|
needsConcat = renderCastedArgument( sqlAppender, translator, expressions.get( i ) );
|
||||||
|
@ -175,7 +201,12 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
sqlAppender.appendSql( concatOperator );
|
sqlAppender.appendSql( concatOperator );
|
||||||
sqlAppender.appendSql( "''" );
|
sqlAppender.appendSql( "''" );
|
||||||
}
|
}
|
||||||
sqlAppender.appendSql( ",'\\0'),''),'\\0" );
|
sqlAppender.appendSql( SqlAppender.COMA_SEPARATOR_CHAR );
|
||||||
|
chr.render( sqlAppender, chrArguments, translator );
|
||||||
|
sqlAppender.appendSql( "),'')," );
|
||||||
|
chr.render( sqlAppender, chrArguments, translator );
|
||||||
|
sqlAppender.appendSql( concatOperator );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
sqlAppender.appendSql( argumentNumber );
|
sqlAppender.appendSql( argumentNumber );
|
||||||
sqlAppender.appendSql( "')" );
|
sqlAppender.appendSql( "')" );
|
||||||
if ( castDistinctStringConcat ) {
|
if ( castDistinctStringConcat ) {
|
||||||
|
|
|
@ -32,6 +32,11 @@ public interface Stack<T> {
|
||||||
*/
|
*/
|
||||||
T getCurrent();
|
T getCurrent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element currently at the bottom of the stack
|
||||||
|
*/
|
||||||
|
T getRoot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many elements are currently on the stack?
|
* How many elements are currently on the stack?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -66,7 +66,15 @@ public final class StandardStack<T> implements Stack<T> {
|
||||||
if ( internalStack == null ) {
|
if ( internalStack == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return convert( internalStack.peek() );
|
return convert( internalStack.peekFirst() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getRoot() {
|
||||||
|
if ( internalStack == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return convert( internalStack.peekLast() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,7 @@ import jakarta.persistence.criteria.Selection;
|
||||||
import jakarta.persistence.criteria.SetJoin;
|
import jakarta.persistence.criteria.SetJoin;
|
||||||
import jakarta.persistence.criteria.Subquery;
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.query.sqm.NullPrecedence;
|
import org.hibernate.query.sqm.NullPrecedence;
|
||||||
import org.hibernate.query.sqm.SortOrder;
|
import org.hibernate.query.sqm.SortOrder;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
@ -104,6 +105,36 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
|
|
||||||
<T> JpaCriteriaQuery<T> except(boolean all, CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries);
|
<T> JpaCriteriaQuery<T> except(boolean all, CriteriaQuery<? extends T> query1, CriteriaQuery<?>... queries);
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> unionAll(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return union( true, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> union(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return union( false, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> JpaSubQuery<T> union(boolean all, Subquery<? extends T> query1, Subquery<?>... queries);
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> intersectAll(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return intersect( true, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> intersect(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return intersect( false, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> JpaSubQuery<T> intersect(boolean all, Subquery<? extends T> query1, Subquery<?>... queries);
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> exceptAll(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return except( true, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> JpaSubQuery<T> except(Subquery<? extends T> query1, Subquery<?>... queries) {
|
||||||
|
return except( false, query1, queries );
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> JpaSubQuery<T> except(boolean all, Subquery<? extends T> query1, Subquery<?>... queries);
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// JPA 3.1
|
// JPA 3.1
|
||||||
|
@ -798,4 +829,65 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
* @return descending ordering corresponding to the expression
|
* @return descending ordering corresponding to the expression
|
||||||
*/
|
*/
|
||||||
JpaOrder desc(Expression<?> x, boolean nullsFirst);
|
JpaOrder desc(Expression<?> x, boolean nullsFirst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering based on the sort order and null precedence of the value of the CTE attribute.
|
||||||
|
* @param cteAttribute CTE attribute used to define the ordering
|
||||||
|
* @param sortOrder The sort order
|
||||||
|
* @param nullPrecedence The null precedence
|
||||||
|
* @return ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder search(JpaCteCriteriaAttribute cteAttribute, SortOrder sortOrder, NullPrecedence nullPrecedence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering based on the sort order of the value of the CTE attribute.
|
||||||
|
* @param cteAttribute CTE attribute used to define the ordering
|
||||||
|
* @param sortOrder The sort order
|
||||||
|
* @return ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder search(JpaCteCriteriaAttribute cteAttribute, SortOrder sortOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering based on the ascending value of the CTE attribute.
|
||||||
|
* @param cteAttribute CTE attribute used to define the ordering
|
||||||
|
* @return ascending ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder search(JpaCteCriteriaAttribute cteAttribute);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering by the ascending value of the CTE attribute.
|
||||||
|
* @param x CTE attribute used to define the ordering
|
||||||
|
* @return ascending ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder asc(JpaCteCriteriaAttribute x);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering by the descending value of the CTE attribute.
|
||||||
|
* @param x CTE attribute used to define the ordering
|
||||||
|
* @return descending ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder desc(JpaCteCriteriaAttribute x);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering by the ascending value of the CTE attribute.
|
||||||
|
* @param x CTE attribute used to define the ordering
|
||||||
|
* @param nullsFirst Whether <code>null</code> should be sorted first
|
||||||
|
* @return ascending ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder asc(JpaCteCriteriaAttribute x, boolean nullsFirst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a search ordering by the descending value of the CTE attribute.
|
||||||
|
* @param x CTE attribute used to define the ordering
|
||||||
|
* @param nullsFirst Whether <code>null</code> should be sorted first
|
||||||
|
* @return descending ordering corresponding to the CTE attribute
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaSearchOrder desc(JpaCteCriteriaAttribute x, boolean nullsFirst);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.AbstractQuery;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common contract for criteria parts that can hold CTEs (common table expressions).
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public interface JpaCteContainer extends JpaCriteriaNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the CTEs that are registered on this container.
|
||||||
|
*/
|
||||||
|
Collection<? extends JpaCteCriteria<?>> getCteCriterias();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CTE that is registered by the given name on this container, or any of its parents.
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> getCteCriteria(String cteName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given {@link CriteriaQuery} and returns a {@link JpaCteCriteria},
|
||||||
|
* which can be used for querying.
|
||||||
|
*
|
||||||
|
* @see JpaCriteriaQuery#from(JpaCteCriteria)
|
||||||
|
* @see JpaFrom#join(JpaCteCriteria, SqmJoinType)
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> with(AbstractQuery<T> criteria);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to register a recursive CTE. The base {@link CriteriaQuery} serves
|
||||||
|
* for the structure of the {@link JpaCteCriteria}, which is made available in the recursive criteria producer function,
|
||||||
|
* so that the recursive {@link CriteriaQuery} is able to refer to the CTE again.
|
||||||
|
*
|
||||||
|
* @see JpaCriteriaQuery#from(JpaCteCriteria)
|
||||||
|
* @see JpaFrom#join(JpaCteCriteria, SqmJoinType)
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> withRecursiveUnionAll(AbstractQuery<T> baseCriteria, Function<JpaCteCriteria<T>, AbstractQuery<T>> recursiveCriteriaProducer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to register a recursive CTE. The base {@link CriteriaQuery} serves
|
||||||
|
* for the structure of the {@link JpaCteCriteria}, which is made available in the recursive criteria producer function,
|
||||||
|
* so that the recursive {@link CriteriaQuery} is able to refer to the CTE again.
|
||||||
|
*
|
||||||
|
* @see JpaCriteriaQuery#from(JpaCteCriteria)
|
||||||
|
* @see JpaFrom#join(JpaCteCriteria, SqmJoinType)
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> withRecursiveUnionDistinct(AbstractQuery<T> baseCriteria, Function<JpaCteCriteria<T>, AbstractQuery<T>> recursiveCriteriaProducer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #with(AbstractQuery)} but assigns an explicit CTE name.
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> with(String name, AbstractQuery<T> criteria);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #withRecursiveUnionAll(AbstractQuery, Function)} but assigns an explicit CTE name.
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> withRecursiveUnionAll(String name, AbstractQuery<T> baseCriteria, Function<JpaCteCriteria<T>, AbstractQuery<T>> recursiveCriteriaProducer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #withRecursiveUnionDistinct(AbstractQuery, Function)} but assigns an explicit CTE name.
|
||||||
|
*/
|
||||||
|
<T> JpaCteCriteria<T> withRecursiveUnionDistinct(String name, AbstractQuery<T> baseCriteria, Function<JpaCteCriteria<T>, AbstractQuery<T>> recursiveCriteriaProducer);
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CTE (common table expression) criteria.
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public interface JpaCteCriteria<T> extends JpaCriteriaNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name under which this CTE is registered.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the CTE.
|
||||||
|
*/
|
||||||
|
JpaCteCriteriaType<T> getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The definition of the CTE.
|
||||||
|
*/
|
||||||
|
JpaSelectCriteria<?> getCteDefinition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container within this CTE is registered.
|
||||||
|
*/
|
||||||
|
JpaCteContainer getCteContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The materialization hint for the CTE.
|
||||||
|
*/
|
||||||
|
CteMaterialization getMaterialization();
|
||||||
|
void setMaterialization(CteMaterialization materialization);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The kind of search (breadth-first or depth-first) that should be done for a recursive query.
|
||||||
|
* May be null if unspecified or if this is not a recursive query.
|
||||||
|
*/
|
||||||
|
CteSearchClauseKind getSearchClauseKind();
|
||||||
|
/**
|
||||||
|
* The order by which should be searched.
|
||||||
|
*/
|
||||||
|
List<JpaSearchOrder> getSearchBySpecifications();
|
||||||
|
/**
|
||||||
|
* The attribute name by which one can order the final CTE result, to achieve the search order.
|
||||||
|
* Note that an implicit {@link JpaCteCriteriaAttribute} will be made available for this.
|
||||||
|
*/
|
||||||
|
String getSearchAttributeName();
|
||||||
|
|
||||||
|
default void search(CteSearchClauseKind kind, String searchAttributeName, JpaSearchOrder... searchOrders) {
|
||||||
|
search( kind, searchAttributeName, Arrays.asList( searchOrders ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void search(CteSearchClauseKind kind, String searchAttributeName, List<JpaSearchOrder> searchOrders);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes to use for cycle detection.
|
||||||
|
*/
|
||||||
|
List<JpaCteCriteriaAttribute> getCycleAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attribute name which is used to mark when a cycle has been detected.
|
||||||
|
* Note that an implicit {@link JpaCteCriteriaAttribute} will be made available for this.
|
||||||
|
*/
|
||||||
|
String getCycleMarkAttributeName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attribute name that represents the computation path, which is used for cycle detection.
|
||||||
|
* Note that an implicit {@link JpaCteCriteriaAttribute} will be made available for this.
|
||||||
|
*/
|
||||||
|
String getCyclePathAttributeName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value which is set for the cycle mark attribute when a cycle is detected.
|
||||||
|
*/
|
||||||
|
Object getCycleValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value for the cycle mark attribute when no cycle is detected.
|
||||||
|
*/
|
||||||
|
Object getNoCycleValue();
|
||||||
|
|
||||||
|
default void cycle(String cycleMarkAttributeName, JpaCteCriteriaAttribute... cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, null, Arrays.asList( cycleColumns ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
default void cycle(String cycleMarkAttributeName, List<JpaCteCriteriaAttribute> cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, null, true, false, cycleColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
default void cycleUsing(String cycleMarkAttributeName, String cyclePathAttributeName, JpaCteCriteriaAttribute... cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, cyclePathAttributeName, Arrays.asList( cycleColumns ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
default void cycleUsing(String cycleMarkAttributeName, String cyclePathAttributeName, List<JpaCteCriteriaAttribute> cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, cyclePathAttributeName, true, false, cycleColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <X> void cycle(String cycleMarkAttributeName, X cycleValue, X noCycleValue, JpaCteCriteriaAttribute... cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, null, cycleValue, noCycleValue, Arrays.asList( cycleColumns ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <X> void cycle(String cycleMarkAttributeName, X cycleValue, X noCycleValue, List<JpaCteCriteriaAttribute> cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, null, cycleValue, noCycleValue, cycleColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
default <X> void cycleUsing(String cycleMarkAttributeName, String cyclePathAttributeName, X cycleValue, X noCycleValue, JpaCteCriteriaAttribute... cycleColumns) {
|
||||||
|
cycleUsing( cycleMarkAttributeName, cyclePathAttributeName, cycleValue, noCycleValue, Arrays.asList( cycleColumns ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
<X> void cycleUsing(String cycleMarkAttributeName, String cyclePathAttributeName, X cycleValue, X noCycleValue, List<JpaCteCriteriaAttribute> cycleColumns);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the attribute of a {@link JpaCteCriteriaType}.
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public interface JpaCteCriteriaAttribute extends JpaCriteriaNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The declaring type.
|
||||||
|
*/
|
||||||
|
JpaCteCriteriaType<?> getDeclaringType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The java type of the attribute.
|
||||||
|
*/
|
||||||
|
Class<?> getJavaType();
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CTE (common table expression) criteria type.
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public interface JpaCteCriteriaType<T> extends JpaCriteriaNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name under which this CTE is registered.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The domain type of the CTE.
|
||||||
|
*/
|
||||||
|
DomainType<T> getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes of the CTE type.
|
||||||
|
*/
|
||||||
|
List<JpaCteCriteriaAttribute> getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the found attribute or null.
|
||||||
|
*/
|
||||||
|
JpaCteCriteriaAttribute getAttribute(String name);
|
||||||
|
}
|
|
@ -9,12 +9,14 @@ package org.hibernate.query.criteria;
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Expression;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
@Incubating
|
@Incubating
|
||||||
public interface JpaDerivedJoin<T> extends JpaDerivedFrom<T>, SqmQualifiedJoin<T,T>, JpaJoinedFrom<T,T> {
|
public interface JpaDerivedJoin<T> extends JpaDerivedFrom<T>, JpaJoinedFrom<T,T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the subquery part can access previous from node aliases.
|
* Specifies whether the subquery part can access previous from node aliases.
|
||||||
* Normally, subqueries in the from clause are unable to access other from nodes,
|
* Normally, subqueries in the from clause are unable to access other from nodes,
|
||||||
|
@ -23,4 +25,16 @@ public interface JpaDerivedJoin<T> extends JpaDerivedFrom<T>, SqmQualifiedJoin<T
|
||||||
*/
|
*/
|
||||||
boolean isLateral();
|
boolean isLateral();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaDerivedJoin<T> on(JpaExpression<Boolean> restriction);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaDerivedJoin<T> on(Expression<Boolean> restriction);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaDerivedJoin<T> on(JpaPredicate... restrictions);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaDerivedJoin<T> on(Predicate... restrictions);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,25 @@ package org.hibernate.query.criteria;
|
||||||
|
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Expression;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface JpaEntityJoin<T> extends JpaJoinedFrom<T,T> {
|
public interface JpaEntityJoin<T> extends JpaJoinedFrom<T,T> {
|
||||||
@Override
|
@Override
|
||||||
EntityDomainType<T> getModel();
|
EntityDomainType<T> getModel();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaEntityJoin<T> on(JpaExpression<Boolean> restriction);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaEntityJoin<T> on(Expression<Boolean> restriction);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaEntityJoin<T> on(JpaPredicate... restrictions);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
JpaEntityJoin<T> on(Predicate... restrictions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,19 @@ import org.hibernate.Incubating;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.CollectionJoin;
|
||||||
import jakarta.persistence.criteria.From;
|
import jakarta.persistence.criteria.From;
|
||||||
|
import jakarta.persistence.criteria.Join;
|
||||||
|
import jakarta.persistence.criteria.JoinType;
|
||||||
|
import jakarta.persistence.criteria.ListJoin;
|
||||||
|
import jakarta.persistence.criteria.MapJoin;
|
||||||
|
import jakarta.persistence.criteria.SetJoin;
|
||||||
import jakarta.persistence.criteria.Subquery;
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
import jakarta.persistence.metamodel.CollectionAttribute;
|
||||||
|
import jakarta.persistence.metamodel.ListAttribute;
|
||||||
|
import jakarta.persistence.metamodel.MapAttribute;
|
||||||
|
import jakarta.persistence.metamodel.SetAttribute;
|
||||||
|
import jakarta.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API extension to the JPA {@link From} contract
|
* API extension to the JPA {@link From} contract
|
||||||
|
@ -45,4 +56,71 @@ public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T>
|
||||||
@Incubating
|
@Incubating
|
||||||
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType, boolean lateral);
|
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType, boolean lateral);
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
<X> JpaJoinedFrom<?, X> join(JpaCteCriteria<X> cte);
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
<X> JpaJoinedFrom<?, X> join(JpaCteCriteria<X> cte, SqmJoinType joinType);
|
||||||
|
|
||||||
|
// Covariant overrides
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaJoin<T, Y> join(SingularAttribute<? super T, Y> attribute);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaJoin<T, Y> join(SingularAttribute<? super T, Y> attribute, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaCollectionJoin<T, Y> join(CollectionAttribute<? super T, Y> collection);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaSetJoin<T, Y> join(SetAttribute<? super T, Y> set);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaListJoin<T, Y> join(ListAttribute<? super T, Y> list);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<K, V> JpaMapJoin<T, K, V> join(MapAttribute<? super T, K, V> map);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaCollectionJoin<T, Y> join(CollectionAttribute<? super T, Y> collection, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaSetJoin<T, Y> join(SetAttribute<? super T, Y> set, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaListJoin<T, Y> join(ListAttribute<? super T, Y> list, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<K, V> JpaMapJoin<T, K, V> join(MapAttribute<? super T, K, V> map, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaJoin<X, Y> join(String attributeName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaCollectionJoin<X, Y> joinCollection(String attributeName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaSetJoin<X, Y> joinSet(String attributeName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaListJoin<X, Y> joinList(String attributeName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, K, V> JpaMapJoin<X, K, V> joinMap(String attributeName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaJoin<X, Y> join(String attributeName, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaCollectionJoin<X, Y> joinCollection(String attributeName, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaSetJoin<X, Y> joinSet(String attributeName, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaListJoin<X, Y> joinList(String attributeName, JoinType jt);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, K, V> JpaMapJoin<X, K, V> joinMap(String attributeName, JoinType jt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,29 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.criteria;
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Expression;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exists within the hierarchy mainly to support "entity joins".
|
* Exists within the hierarchy mainly to support "entity joins".
|
||||||
*
|
*
|
||||||
* @see JpaEntityJoin
|
* @see JpaEntityJoin
|
||||||
* @see org.hibernate.query.sqm.tree.from.SqmEntityJoin
|
* @see org.hibernate.query.sqm.tree.from.SqmEntityJoin
|
||||||
|
* @see JpaDerivedJoin
|
||||||
|
* @see org.hibernate.query.sqm.tree.from.SqmDerivedJoin
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface JpaJoinedFrom<O,T> extends JpaFrom<O, T> {
|
public interface JpaJoinedFrom<O,T> extends JpaFrom<O, T> {
|
||||||
|
|
||||||
|
JpaJoinedFrom<O, T> on(JpaExpression<Boolean> restriction);
|
||||||
|
|
||||||
|
JpaJoinedFrom<O, T> on(Expression<Boolean> restriction);
|
||||||
|
|
||||||
|
JpaJoinedFrom<O, T> on(JpaPredicate... restrictions);
|
||||||
|
|
||||||
|
JpaJoinedFrom<O, T> on(Predicate... restrictions);
|
||||||
|
|
||||||
|
JpaPredicate getOn();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,5 @@ import jakarta.persistence.criteria.CommonAbstractCriteria;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface JpaQueryableCriteria<T> extends JpaCriteriaBase, JpaCriteriaNode {
|
public interface JpaQueryableCriteria<T> extends JpaCriteriaBase, JpaCriteriaNode, JpaCteContainer {
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.query.sqm.NullPrecedence;
|
||||||
|
import org.hibernate.query.sqm.SortOrder;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the search order for a recursive CTE (common table expression).
|
||||||
|
*
|
||||||
|
* @see JpaCteCriteria
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public interface JpaSearchOrder extends JpaCriteriaNode {
|
||||||
|
SortOrder getSortOrder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the precedence for nulls for this search order element
|
||||||
|
*/
|
||||||
|
JpaSearchOrder nullPrecedence(NullPrecedence precedence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The precedence for nulls for this search order element
|
||||||
|
*/
|
||||||
|
NullPrecedence getNullPrecedence();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether ascending ordering is in effect.
|
||||||
|
* @return boolean indicating whether ordering is ascending
|
||||||
|
*/
|
||||||
|
boolean isAscending();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the ordering.
|
||||||
|
* @return a new <code>Order</code> instance with the reversed ordering
|
||||||
|
*/
|
||||||
|
JpaSearchOrder reverse();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CTE attribute that is used for ordering.
|
||||||
|
* @return CTE attribute used for ordering
|
||||||
|
*/
|
||||||
|
JpaCteCriteriaAttribute getAttribute();
|
||||||
|
}
|
|
@ -38,6 +38,15 @@ public interface JpaSelectCriteria<T> extends AbstractQuery<T>, JpaCriteriaBase
|
||||||
*/
|
*/
|
||||||
<X> JpaDerivedRoot<X> from(Subquery<X> subquery);
|
<X> JpaDerivedRoot<X> from(Subquery<X> subquery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and add a query root corresponding to the given cte,
|
||||||
|
* forming a cartesian product with any existing roots.
|
||||||
|
*
|
||||||
|
* @param cte the cte criteria
|
||||||
|
* @return query root corresponding to the given cte
|
||||||
|
*/
|
||||||
|
<X> JpaRoot<X> from(JpaCteCriteria<X> cte);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaSelectCriteria<T> distinct(boolean distinct);
|
JpaSelectCriteria<T> distinct(boolean distinct);
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,17 @@ package org.hibernate.query.criteria;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.CollectionJoin;
|
||||||
import jakarta.persistence.criteria.Expression;
|
import jakarta.persistence.criteria.Expression;
|
||||||
|
import jakarta.persistence.criteria.Join;
|
||||||
|
import jakarta.persistence.criteria.ListJoin;
|
||||||
|
import jakarta.persistence.criteria.MapJoin;
|
||||||
import jakarta.persistence.criteria.Order;
|
import jakarta.persistence.criteria.Order;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
import jakarta.persistence.criteria.Selection;
|
import jakarta.persistence.criteria.Selection;
|
||||||
|
import jakarta.persistence.criteria.SetJoin;
|
||||||
import jakarta.persistence.criteria.Subquery;
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
|
@ -22,7 +29,7 @@ import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface JpaSubQuery<T> extends Subquery<T>, JpaSelectCriteria<T>, JpaExpression<T> {
|
public interface JpaSubQuery<T> extends Subquery<T>, JpaSelectCriteria<T>, JpaExpression<T>, JpaCteContainer {
|
||||||
|
|
||||||
JpaSubQuery<T> multiselect(Selection<?>... selections);
|
JpaSubQuery<T> multiselect(Selection<?>... selections);
|
||||||
|
|
||||||
|
@ -93,4 +100,22 @@ public interface JpaSubQuery<T> extends Subquery<T>, JpaSelectCriteria<T>, JpaEx
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaSubQuery<T> having(Predicate... restrictions);
|
JpaSubQuery<T> having(Predicate... restrictions);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<Y> JpaRoot<Y> correlate(Root<Y> parentRoot);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaJoin<X, Y> correlate(Join<X, Y> parentJoin);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaCollectionJoin<X, Y> correlate(CollectionJoin<X, Y> parentCollection);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaSetJoin<X, Y> correlate(SetJoin<X, Y> parentSet);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, Y> JpaListJoin<X, Y> correlate(ListJoin<X, Y> parentList);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<X, K, V> JpaMapJoin<X, K, V> correlate(MapJoin<X, K, V> parentMap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,52 +227,40 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
||||||
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
|
||||||
|
|
||||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
||||||
final boolean canUseInnerJoin = joinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
|
|
||||||
final EntityPersister entityPersister = delegate.getEntityMappingType().getEntityPersister();
|
|
||||||
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
|
|
||||||
canUseInnerJoin,
|
|
||||||
navigablePath,
|
navigablePath,
|
||||||
fetched,
|
lhs,
|
||||||
() -> createTableGroupInternal(
|
|
||||||
canUseInnerJoin,
|
|
||||||
navigablePath,
|
|
||||||
fetched,
|
|
||||||
null,
|
|
||||||
sqlAliasBase,
|
|
||||||
sqlExpressionResolver,
|
|
||||||
creationContext
|
|
||||||
),
|
|
||||||
(np, tableExpression) -> {
|
|
||||||
if ( !tableExpression.isEmpty() && !entityPersister.containsTableReference( tableExpression ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( navigablePath.equals( np.getParent() ) ) {
|
|
||||||
return targetKeyPropertyNames.contains( np.getLocalName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
final String relativePath = np.relativize( navigablePath );
|
|
||||||
if ( relativePath == null ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty relative path means the navigable paths are equal,
|
|
||||||
// in which case we allow resolving the parent table group
|
|
||||||
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
|
|
||||||
},
|
|
||||||
this,
|
|
||||||
explicitSourceAlias,
|
explicitSourceAlias,
|
||||||
sqlAliasBase,
|
requestedJoinType,
|
||||||
creationContext.getSessionFactory(),
|
fetched,
|
||||||
lhs
|
null,
|
||||||
|
aliasBaseGenerator,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
fromClauseAccess,
|
||||||
|
creationContext
|
||||||
);
|
);
|
||||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||||
lazyTableGroup.getNavigablePath(),
|
navigablePath,
|
||||||
joinType,
|
joinType,
|
||||||
lazyTableGroup,
|
lazyTableGroup,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
lazyTableGroup.setTableGroupInitializerCallback(
|
||||||
|
createTableGroupInitializerCallback(
|
||||||
|
lhs,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
sessionFactory,
|
||||||
|
tableGroupJoin::applyPredicate
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return tableGroupJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<TableGroup> createTableGroupInitializerCallback(
|
||||||
|
TableGroup lhs,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
SessionFactoryImplementor sessionFactory,
|
||||||
|
Consumer<Predicate> predicateConsumer) {
|
||||||
// -----------------
|
// -----------------
|
||||||
// Collect the selectable mappings for the FK key side and target side
|
// Collect the selectable mappings for the FK key side and target side
|
||||||
// As we will "resolve" the derived column references for these mappings
|
// As we will "resolve" the derived column references for these mappings
|
||||||
|
@ -350,8 +338,7 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
lazyTableGroup.setTableGroupInitializerCallback(
|
Consumer<TableGroup> tableGroupInitializerCallback = tg -> {
|
||||||
tg -> {
|
|
||||||
this.identifierMapping.forEachSelectable(
|
this.identifierMapping.forEachSelectable(
|
||||||
(i, selectableMapping) -> {
|
(i, selectableMapping) -> {
|
||||||
final SelectableMapping targetMapping = targetMappings.get( i );
|
final SelectableMapping targetMapping = targetMappings.get( i );
|
||||||
|
@ -360,7 +347,7 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
targetMapping.getContainingTableExpression(),
|
targetMapping.getContainingTableExpression(),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
tableGroupJoin.applyPredicate(
|
predicateConsumer.accept(
|
||||||
new ComparisonPredicate(
|
new ComparisonPredicate(
|
||||||
keyColumnReferences.get( i ),
|
keyColumnReferences.get( i ),
|
||||||
ComparisonOperator.EQUAL,
|
ComparisonOperator.EQUAL,
|
||||||
|
@ -373,9 +360,8 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
return tableGroupInitializerCallback;
|
||||||
return tableGroupJoin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableGroup createTableGroupInternal(
|
public TableGroup createTableGroupInternal(
|
||||||
|
@ -415,7 +401,7 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableGroup createRootTableGroupJoin(
|
public LazyTableGroup createRootTableGroupJoin(
|
||||||
NavigablePath navigablePath,
|
NavigablePath navigablePath,
|
||||||
TableGroup lhs,
|
TableGroup lhs,
|
||||||
String explicitSourceAlias,
|
String explicitSourceAlias,
|
||||||
|
@ -426,18 +412,58 @@ public class AnonymousTupleEntityValuedModelPart implements EntityValuedModelPar
|
||||||
SqlExpressionResolver sqlExpressionResolver,
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
FromClauseAccess fromClauseAccess,
|
FromClauseAccess fromClauseAccess,
|
||||||
SqlAstCreationContext creationContext) {
|
SqlAstCreationContext creationContext) {
|
||||||
return ( (TableGroupJoinProducer) delegate ).createRootTableGroupJoin(
|
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||||
|
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
|
||||||
|
final EntityPersister entityPersister = delegate.getEntityMappingType().getEntityPersister();
|
||||||
|
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
|
||||||
|
canUseInnerJoin,
|
||||||
navigablePath,
|
navigablePath,
|
||||||
lhs,
|
|
||||||
explicitSourceAlias,
|
|
||||||
sqlAstJoinType,
|
|
||||||
fetched,
|
fetched,
|
||||||
predicateConsumer,
|
() -> createTableGroupInternal(
|
||||||
aliasBaseGenerator,
|
canUseInnerJoin,
|
||||||
sqlExpressionResolver,
|
navigablePath,
|
||||||
fromClauseAccess,
|
fetched,
|
||||||
creationContext
|
null,
|
||||||
|
sqlAliasBase,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
creationContext
|
||||||
|
),
|
||||||
|
(np, tableExpression) -> {
|
||||||
|
if ( !tableExpression.isEmpty() && !entityPersister.containsTableReference( tableExpression ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( navigablePath.equals( np.getParent() ) ) {
|
||||||
|
return targetKeyPropertyNames.contains( np.getLocalName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
final String relativePath = np.relativize( navigablePath );
|
||||||
|
if ( relativePath == null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty relative path means the navigable paths are equal,
|
||||||
|
// in which case we allow resolving the parent table group
|
||||||
|
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
|
||||||
|
},
|
||||||
|
this,
|
||||||
|
explicitSourceAlias,
|
||||||
|
sqlAliasBase,
|
||||||
|
creationContext.getSessionFactory(),
|
||||||
|
lhs
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( predicateConsumer != null ) {
|
||||||
|
lazyTableGroup.setTableGroupInitializerCallback(
|
||||||
|
createTableGroupInitializerCallback(
|
||||||
|
lhs,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
creationContext.getSessionFactory(),
|
||||||
|
predicateConsumer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lazyTableGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
package org.hibernate.query.derived;
|
package org.hibernate.query.derived;
|
||||||
|
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.PersistentAttribute;
|
import org.hibernate.metamodel.model.domain.PersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
|
||||||
|
@ -19,6 +21,7 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,8 +79,8 @@ public class AnonymousTupleSqmPathSource<J> implements SqmPathSource<J> {
|
||||||
else {
|
else {
|
||||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||||
}
|
}
|
||||||
final SqmPathSource<J> nodeType = path.getNodeType();
|
final DomainType<?> domainType = path.getNodeType().getSqmPathType();
|
||||||
if ( nodeType instanceof BasicSqmPathSource<?> ) {
|
if ( domainType instanceof BasicDomainType<?> ) {
|
||||||
return new SqmBasicValuedSimplePath<>(
|
return new SqmBasicValuedSimplePath<>(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
this,
|
this,
|
||||||
|
@ -85,7 +88,7 @@ public class AnonymousTupleSqmPathSource<J> implements SqmPathSource<J> {
|
||||||
lhs.nodeBuilder()
|
lhs.nodeBuilder()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( nodeType instanceof EmbeddedSqmPathSource<?> ) {
|
else if ( domainType instanceof EmbeddableDomainType<?> ) {
|
||||||
return new SqmEmbeddedValuedSimplePath<>(
|
return new SqmEmbeddedValuedSimplePath<>(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
this,
|
this,
|
||||||
|
@ -93,8 +96,7 @@ public class AnonymousTupleSqmPathSource<J> implements SqmPathSource<J> {
|
||||||
lhs.nodeBuilder()
|
lhs.nodeBuilder()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( nodeType instanceof EntitySqmPathSource<?> || nodeType instanceof EntityDomainType<?>
|
else if ( domainType instanceof EntityDomainType<?> ) {
|
||||||
|| nodeType instanceof PersistentAttribute<?, ?> && nodeType.getSqmPathType() instanceof EntityDomainType<?> ) {
|
|
||||||
return new SqmEntityValuedSimplePath<>(
|
return new SqmEntityValuedSimplePath<>(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
this,
|
this,
|
||||||
|
@ -103,6 +105,6 @@ public class AnonymousTupleSqmPathSource<J> implements SqmPathSource<J> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnsupportedOperationException( "Unsupported path source: " + nodeType );
|
throw new UnsupportedOperationException( "Unsupported path source: " + domainType );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.derived;
|
package org.hibernate.query.derived;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -29,6 +30,7 @@ import org.hibernate.metamodel.mapping.MappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
|
@ -43,6 +45,7 @@ import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
||||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
@ -304,6 +307,10 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, ModelPart> getModelParts() {
|
||||||
|
return modelParts;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSqlAliasStem() {
|
public String getSqlAliasStem() {
|
||||||
return aliasStem;
|
return aliasStem;
|
||||||
|
@ -314,6 +321,16 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
|
||||||
return javaTypeDescriptor;
|
return javaTypeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int forEachSelectable(int offset, SelectableConsumer consumer) {
|
||||||
|
final int originalOffset = offset;
|
||||||
|
for ( ModelPart modelPart : modelParts.values() ) {
|
||||||
|
offset += modelPart.forEachSelectable( offset, consumer );
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset - originalOffset;
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------
|
//--------------------------------
|
||||||
// Support for using the anonymous tuple as table reference directly somewhere is not yet implemented
|
// Support for using the anonymous tuple as table reference directly somewhere is not yet implemented
|
||||||
//--------------------------------
|
//--------------------------------
|
||||||
|
@ -371,5 +388,4 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
|
||||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||||
throw new UnsupportedOperationException( "Not yet implemented" );
|
throw new UnsupportedOperationException( "Not yet implemented" );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.UnsupportedMappingException;
|
import org.hibernate.metamodel.UnsupportedMappingException;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
import org.hibernate.metamodel.model.domain.SimpleDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||||
|
@ -32,6 +33,8 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.ObjectArrayJavaType;
|
import org.hibernate.type.descriptor.java.ObjectArrayJavaType;
|
||||||
|
|
||||||
|
import jakarta.persistence.metamodel.Attribute;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
|
@ -88,6 +91,53 @@ public class AnonymousTupleType<T> implements TupleType<T>, DomainType<T>, Retur
|
||||||
return new AnonymousTupleTableGroupProducer( this, aliasStem, sqlSelections, fromClauseAccess );
|
return new AnonymousTupleTableGroupProducer( this, aliasStem, sqlSelections, fromClauseAccess );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> determineColumnNames() {
|
||||||
|
final int componentCount = componentCount();
|
||||||
|
final List<String> columnNames = new ArrayList<>( componentCount );
|
||||||
|
for ( int i = 0; i < componentCount; i++ ) {
|
||||||
|
final SqmSelectableNode<?> selectableNode = getSelectableNode( i );
|
||||||
|
final String componentName = getComponentName( i );
|
||||||
|
if ( selectableNode instanceof SqmPath<?> ) {
|
||||||
|
addColumnNames(
|
||||||
|
columnNames,
|
||||||
|
( (SqmPath<?>) selectableNode ).getNodeType().getSqmPathType(),
|
||||||
|
componentName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columnNames.add( componentName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columnNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addColumnNames(List<String> columnNames, DomainType<?> domainType, String componentName) {
|
||||||
|
if ( domainType instanceof EntityDomainType<?> ) {
|
||||||
|
final EntityDomainType<?> entityDomainType = (EntityDomainType<?>) domainType;
|
||||||
|
final SingularPersistentAttribute<?, ?> idAttribute = entityDomainType.findIdAttribute();
|
||||||
|
final String idPath;
|
||||||
|
if ( idAttribute == null ) {
|
||||||
|
idPath = componentName;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idPath = componentName + "_" + idAttribute.getName();
|
||||||
|
}
|
||||||
|
addColumnNames( columnNames, entityDomainType.getIdentifierDescriptor().getSqmPathType(), idPath );
|
||||||
|
}
|
||||||
|
else if ( domainType instanceof ManagedDomainType<?> ) {
|
||||||
|
for ( Attribute<?, ?> attribute : ( (ManagedDomainType<?>) domainType ).getAttributes() ) {
|
||||||
|
if ( !( attribute instanceof SingularPersistentAttribute<?, ?> ) ) {
|
||||||
|
throw new IllegalArgumentException( "Only embeddables without collections are supported" );
|
||||||
|
}
|
||||||
|
final DomainType<?> attributeType = ( (SingularPersistentAttribute<?, ?>) attribute ).getType();
|
||||||
|
addColumnNames( columnNames, attributeType, componentName + "_" + attribute.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columnNames.add( componentName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int componentCount() {
|
public int componentCount() {
|
||||||
return components.length;
|
return components.length;
|
||||||
|
@ -114,6 +164,10 @@ public class AnonymousTupleType<T> implements TupleType<T>, DomainType<T>, Retur
|
||||||
return index == null ? null : components[index].getExpressible();
|
return index == null ? null : components[index].getExpressible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Integer getIndex(String componentName) {
|
||||||
|
return componentIndexMap.get( componentName );
|
||||||
|
}
|
||||||
|
|
||||||
public SqmSelectableNode<?> getSelectableNode(int index) {
|
public SqmSelectableNode<?> getSelectableNode(int index) {
|
||||||
return components[index];
|
return components[index];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.derived;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteTable;
|
||||||
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table group producer for a CTE tuple type.
|
||||||
|
*
|
||||||
|
* Exposes additional access to some special model parts for recursive CTE attributes.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public class CteTupleTableGroupProducer extends AnonymousTupleTableGroupProducer {
|
||||||
|
|
||||||
|
private final AnonymousTupleBasicValuedModelPart searchModelPart;
|
||||||
|
private final AnonymousTupleBasicValuedModelPart cycleMarkModelPart;
|
||||||
|
private final AnonymousTupleBasicValuedModelPart cyclePathModelPart;
|
||||||
|
|
||||||
|
public CteTupleTableGroupProducer(
|
||||||
|
SqmCteTable<?> sqmCteTable,
|
||||||
|
String aliasStem,
|
||||||
|
List<SqlSelection> sqlSelections,
|
||||||
|
FromClauseAccess fromClauseAccess) {
|
||||||
|
super( sqmCteTable, aliasStem, sqlSelections, fromClauseAccess );
|
||||||
|
final SqmCteStatement<?> cteStatement = sqmCteTable.getCteStatement();
|
||||||
|
final BasicType<String> stringType = cteStatement.nodeBuilder()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getBasicTypeForJavaType( String.class );
|
||||||
|
this.searchModelPart = createModelPart( cteStatement.getSearchAttributeName(), stringType );
|
||||||
|
this.cycleMarkModelPart = createModelPart(
|
||||||
|
cteStatement.getCycleMarkAttributeName(),
|
||||||
|
cteStatement.getCycleLiteral() == null
|
||||||
|
? null
|
||||||
|
: (BasicType<?>) cteStatement.getCycleLiteral().getNodeType()
|
||||||
|
);
|
||||||
|
this.cyclePathModelPart = createModelPart( cteStatement.getCyclePathAttributeName(), stringType );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnonymousTupleBasicValuedModelPart createModelPart(String attributeName, BasicType<?> basicType) {
|
||||||
|
if ( attributeName != null ) {
|
||||||
|
return new AnonymousTupleBasicValuedModelPart(
|
||||||
|
attributeName,
|
||||||
|
attributeName,
|
||||||
|
basicType,
|
||||||
|
basicType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CteColumn> determineCteColumns() {
|
||||||
|
final List<CteColumn> columns = new ArrayList<>( getModelParts().size() );
|
||||||
|
forEachSelectable(
|
||||||
|
(selectionIndex, selectableMapping) -> {
|
||||||
|
columns.add(
|
||||||
|
new CteColumn(
|
||||||
|
selectableMapping.getSelectionExpression(),
|
||||||
|
selectableMapping.getJdbcMapping()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||||
|
final ModelPart subPart = super.findSubPart( name, treatTargetType );
|
||||||
|
if ( subPart != null ) {
|
||||||
|
return subPart;
|
||||||
|
}
|
||||||
|
if ( searchModelPart != null && name.equals( searchModelPart.getPartName() ) ) {
|
||||||
|
return searchModelPart;
|
||||||
|
}
|
||||||
|
if ( cycleMarkModelPart != null && name.equals( cycleMarkModelPart.getPartName() ) ) {
|
||||||
|
return cycleMarkModelPart;
|
||||||
|
}
|
||||||
|
if ( cyclePathModelPart != null && name.equals( cyclePathModelPart.getPartName() ) ) {
|
||||||
|
return cyclePathModelPart;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,7 +17,11 @@ import org.hibernate.query.sqm.SqmJoinable;
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmCteRoot;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmCteJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
|
@ -270,7 +274,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
|
|
||||||
private final StringBuilder path = new StringBuilder();
|
private final StringBuilder path = new StringBuilder();
|
||||||
|
|
||||||
private SqmEntityJoin<?> join;
|
private SqmPath<?> join;
|
||||||
|
|
||||||
public ExpectingEntityJoinDelegate(
|
public ExpectingEntityJoinDelegate(
|
||||||
String identifier,
|
String identifier,
|
||||||
|
@ -301,6 +305,12 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
.getJpaMetamodel()
|
.getJpaMetamodel()
|
||||||
.resolveHqlEntityReference( fullPath );
|
.resolveHqlEntityReference( fullPath );
|
||||||
if ( joinedEntityType == null ) {
|
if ( joinedEntityType == null ) {
|
||||||
|
final SqmCteStatement<?> cteStatement = creationState.findCteStatement( fullPath );
|
||||||
|
if ( cteStatement != null ) {
|
||||||
|
join = new SqmCteJoin<>( cteStatement, alias, joinType, sqmRoot );
|
||||||
|
creationState.getCurrentProcessingState().getPathRegistry().register( join );
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw new SemanticException( "Could not resolve join path - " + fullPath );
|
throw new SemanticException( "Could not resolve join path - " + fullPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,22 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.query.criteria.JpaCteCriteria;
|
||||||
|
import org.hibernate.query.criteria.JpaCteCriteriaAttribute;
|
||||||
|
import org.hibernate.query.criteria.JpaSearchOrder;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteTableColumn;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmSearchClauseSpecification;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmCteRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmCteJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
|
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationOptions;
|
import org.hibernate.query.hql.spi.SqmCreationOptions;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
|
@ -81,6 +90,9 @@ import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.AbstractQuery;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles splitting queries containing unmapped polymorphic references.
|
* Handles splitting queries containing unmapped polymorphic references.
|
||||||
*
|
*
|
||||||
|
@ -215,17 +227,129 @@ public class QuerySplitter {
|
||||||
public Object visitCteContainer(SqmCteContainer consumer) {
|
public Object visitCteContainer(SqmCteContainer consumer) {
|
||||||
final SqmCteContainer processingQuery = (SqmCteContainer) getProcessingStateStack().getCurrent()
|
final SqmCteContainer processingQuery = (SqmCteContainer) getProcessingStateStack().getCurrent()
|
||||||
.getProcessingQuery();
|
.getProcessingQuery();
|
||||||
processingQuery.setWithRecursive( consumer.isWithRecursive() );
|
|
||||||
for ( SqmCteStatement<?> cteStatement : consumer.getCteStatements() ) {
|
for ( SqmCteStatement<?> cteStatement : consumer.getCteStatements() ) {
|
||||||
processingQuery.addCteStatement( visitCteStatement( cteStatement ) );
|
visitCteStatement( cteStatement );
|
||||||
}
|
}
|
||||||
return processingQuery;
|
return processingQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmCteStatement<?> visitCteStatement(SqmCteStatement<?> sqmCteStatement) {
|
public SqmCteStatement<?> visitCteStatement(SqmCteStatement<?> sqmCteStatement) {
|
||||||
// No need to copy anything here
|
final SqmCteContainer processingQuery = (SqmCteContainer) getProcessingStateStack().getCurrent()
|
||||||
return sqmCteStatement;
|
.getProcessingQuery();
|
||||||
|
final SqmSelectQuery<?> cteDefinition = sqmCteStatement.getCteDefinition();
|
||||||
|
final SqmQueryPart<?> queryPart = cteDefinition.getQueryPart();
|
||||||
|
JpaCteCriteria<?> cteCriteria = null;
|
||||||
|
if ( cteDefinition.getCteStatements().isEmpty()
|
||||||
|
&& queryPart instanceof SqmQueryGroup<?> && queryPart.getSortSpecifications().isEmpty()
|
||||||
|
&& queryPart.getFetchExpression() == null && queryPart.getOffsetExpression() == null ) {
|
||||||
|
final SqmQueryGroup<?> queryGroup = (SqmQueryGroup<?>) queryPart;
|
||||||
|
boolean unionDistinct = false;
|
||||||
|
switch ( queryGroup.getSetOperator() ) {
|
||||||
|
case UNION:
|
||||||
|
unionDistinct = true;
|
||||||
|
case UNION_ALL:
|
||||||
|
if ( queryGroup.getQueryParts().size() == 2 ) {
|
||||||
|
final SqmSelectQuery<Object> nonRecursiveStatement = visitSelectQuery(
|
||||||
|
cteDefinition,
|
||||||
|
queryGroup.getQueryParts().get( 0 )
|
||||||
|
);
|
||||||
|
final Function<JpaCteCriteria<Object>, AbstractQuery<Object>> recursiveCriteriaProducer = jpaCteCriteria -> {
|
||||||
|
return visitSelectQuery( cteDefinition, queryGroup.getQueryParts().get( 1 ) );
|
||||||
|
};
|
||||||
|
if ( sqmCteStatement.getName() == null ) {
|
||||||
|
if ( unionDistinct ) {
|
||||||
|
cteCriteria = processingQuery.withRecursiveUnionDistinct(
|
||||||
|
nonRecursiveStatement,
|
||||||
|
recursiveCriteriaProducer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cteCriteria = processingQuery.withRecursiveUnionAll(
|
||||||
|
nonRecursiveStatement,
|
||||||
|
recursiveCriteriaProducer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( unionDistinct ) {
|
||||||
|
cteCriteria = processingQuery.withRecursiveUnionDistinct(
|
||||||
|
sqmCteStatement.getName(),
|
||||||
|
nonRecursiveStatement,
|
||||||
|
recursiveCriteriaProducer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cteCriteria = processingQuery.withRecursiveUnionAll(
|
||||||
|
sqmCteStatement.getName(),
|
||||||
|
nonRecursiveStatement,
|
||||||
|
recursiveCriteriaProducer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sqmCteStatement.getSearchClauseKind() != null ) {
|
||||||
|
final List<JpaSearchOrder> searchBySpecifications = sqmCteStatement.getSearchBySpecifications();
|
||||||
|
final List<JpaSearchOrder> newSearchBySpecifications = new ArrayList<>( searchBySpecifications.size() );
|
||||||
|
for ( JpaSearchOrder searchBySpecification : searchBySpecifications ) {
|
||||||
|
newSearchBySpecifications.add(
|
||||||
|
new SqmSearchClauseSpecification(
|
||||||
|
(SqmCteTableColumn) cteCriteria.getType().getAttribute( searchBySpecification.getAttribute().getName() ),
|
||||||
|
searchBySpecification.getSortOrder(),
|
||||||
|
searchBySpecification.getNullPrecedence()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cteCriteria.search(
|
||||||
|
sqmCteStatement.getSearchClauseKind(),
|
||||||
|
sqmCteStatement.getSearchAttributeName(),
|
||||||
|
newSearchBySpecifications
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( sqmCteStatement.getCycleMarkAttributeName() != null ) {
|
||||||
|
final List<JpaCteCriteriaAttribute> cycleAttributes = sqmCteStatement.getCycleAttributes();
|
||||||
|
final List<JpaCteCriteriaAttribute> newCycleAttributes = new ArrayList<>( cycleAttributes.size() );
|
||||||
|
for ( JpaCteCriteriaAttribute cycleAttribute : cycleAttributes ) {
|
||||||
|
newCycleAttributes.add(
|
||||||
|
cteCriteria.getType().getAttribute( cycleAttribute.getName() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cteCriteria.cycleUsing(
|
||||||
|
sqmCteStatement.getCycleMarkAttributeName(),
|
||||||
|
sqmCteStatement.getCyclePathAttributeName(),
|
||||||
|
sqmCteStatement.getCycleValue(),
|
||||||
|
sqmCteStatement.getNoCycleValue(),
|
||||||
|
newCycleAttributes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( cteCriteria == null ) {
|
||||||
|
if ( sqmCteStatement.getName() == null ) {
|
||||||
|
cteCriteria = processingQuery.with( visitSelectQuery( cteDefinition ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cteCriteria = processingQuery.with( sqmCteStatement.getName(), visitSelectQuery( cteDefinition ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( sqmCteStatement.getMaterialization() != null ) {
|
||||||
|
cteCriteria.setMaterialization( sqmCteStatement.getMaterialization() );
|
||||||
|
}
|
||||||
|
return (SqmCteStatement<?>) cteCriteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmCteStatement<?> findCteStatement(String name) {
|
||||||
|
return processingStateStack.findCurrentFirst(
|
||||||
|
state -> {
|
||||||
|
if ( state.getProcessingQuery() instanceof SqmCteContainer ) {
|
||||||
|
final SqmCteContainer container = (SqmCteContainer) state.getProcessingQuery();
|
||||||
|
return container.getCteStatement( name );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -285,6 +409,43 @@ public class QuerySplitter {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SqmSelectQuery<Object> visitSelectQuery(SqmSelectQuery<?> selectQuery) {
|
||||||
|
if ( selectQuery instanceof SqmSelectStatement<?> ) {
|
||||||
|
return (SqmSelectQuery<Object>) visitSelectStatement( (SqmSelectStatement<?>) selectQuery );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return visitSubQueryExpression( (SqmSubQuery<?>) selectQuery );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SqmSelectQuery<Object> visitSelectQuery(SqmSelectQuery<?> selectQuery, SqmQueryPart<?> queryPart) {
|
||||||
|
if ( selectQuery instanceof SqmSelectStatement<?> ) {
|
||||||
|
final SqmSelectStatement<?> sqmSelectStatement = (SqmSelectStatement<?>) selectQuery;
|
||||||
|
//noinspection rawtypes
|
||||||
|
return (SqmSelectQuery<Object>) visitSelectStatement(
|
||||||
|
new SqmSelectStatement(
|
||||||
|
queryPart,
|
||||||
|
sqmSelectStatement.getResultType(),
|
||||||
|
sqmSelectStatement.getQuerySource(),
|
||||||
|
sqmSelectStatement.nodeBuilder()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final SqmSubQuery<?> sqmSubQuery = (SqmSubQuery<?>) selectQuery;
|
||||||
|
//noinspection rawtypes
|
||||||
|
return visitSubQueryExpression(
|
||||||
|
new SqmSubQuery(
|
||||||
|
processingStateStack.getCurrent().getProcessingQuery(),
|
||||||
|
queryPart,
|
||||||
|
sqmSubQuery.getResultType(),
|
||||||
|
sqmSubQuery.nodeBuilder()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQueryPart<R> visitQueryPart(SqmQueryPart<?> queryPart) {
|
public SqmQueryPart<R> visitQueryPart(SqmQueryPart<?> queryPart) {
|
||||||
return (SqmQueryPart<R>) super.visitQueryPart( queryPart );
|
return (SqmQueryPart<R>) super.visitQueryPart( queryPart );
|
||||||
|
@ -411,6 +572,25 @@ public class QuerySplitter {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmCteRoot<?> visitRootCte(SqmCteRoot<?> sqmRoot) {
|
||||||
|
SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( sqmRoot );
|
||||||
|
if ( sqmFrom != null ) {
|
||||||
|
return (SqmCteRoot<?>) sqmFrom;
|
||||||
|
}
|
||||||
|
final SqmCteRoot<?> copy = new SqmCteRoot<>(
|
||||||
|
(SqmCteStatement<?>) sqmRoot.getCte().accept( this ),
|
||||||
|
sqmRoot.getExplicitAlias()
|
||||||
|
);
|
||||||
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
|
sqmFromCopyMap.put( sqmRoot, copy );
|
||||||
|
sqmPathCopyMap.put( sqmRoot.getNavigablePath(), copy );
|
||||||
|
if ( currentFromClauseCopy != null ) {
|
||||||
|
currentFromClauseCopy.addRoot( copy );
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmCrossJoin<?> visitCrossJoin(SqmCrossJoin<?> join) {
|
public SqmCrossJoin<?> visitCrossJoin(SqmCrossJoin<?> join) {
|
||||||
final SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( join );
|
final SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( join );
|
||||||
|
@ -506,6 +686,26 @@ public class QuerySplitter {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmCteJoin<?> visitQualifiedCteJoin(SqmCteJoin<?> join) {
|
||||||
|
SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( join );
|
||||||
|
if ( sqmFrom != null ) {
|
||||||
|
return (SqmCteJoin<?>) sqmFrom;
|
||||||
|
}
|
||||||
|
final SqmRoot<?> sqmRoot = (SqmRoot<?>) sqmFromCopyMap.get( join.findRoot() );
|
||||||
|
final SqmCteJoin copy = new SqmCteJoin(
|
||||||
|
(SqmCteStatement<?>) join.getCte().accept( this ),
|
||||||
|
join.getExplicitAlias(),
|
||||||
|
join.getSqmJoinType(),
|
||||||
|
sqmRoot
|
||||||
|
);
|
||||||
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
|
sqmFromCopyMap.put( join, copy );
|
||||||
|
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
||||||
|
sqmRoot.addSqmJoin( copy );
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmBasicValuedSimplePath<?> visitBasicValuedPath(SqmBasicValuedSimplePath<?> path) {
|
public SqmBasicValuedSimplePath<?> visitBasicValuedPath(SqmBasicValuedSimplePath<?> path) {
|
||||||
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
||||||
|
|
|
@ -61,6 +61,10 @@ import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
|
||||||
import org.hibernate.query.PathException;
|
import org.hibernate.query.PathException;
|
||||||
import org.hibernate.query.ReturnableType;
|
import org.hibernate.query.ReturnableType;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
|
import org.hibernate.query.criteria.JpaCteCriteria;
|
||||||
|
import org.hibernate.query.criteria.JpaCteCriteriaAttribute;
|
||||||
|
import org.hibernate.query.criteria.JpaCteCriteriaType;
|
||||||
|
import org.hibernate.query.criteria.JpaSearchOrder;
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||||
|
@ -103,9 +107,12 @@ import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
import org.hibernate.query.sqm.tree.SqmQuery;
|
import org.hibernate.query.sqm.tree.SqmQuery;
|
||||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||||
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||||
import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
|
import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmCteRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
|
@ -151,6 +158,7 @@ import org.hibernate.query.sqm.tree.expression.SqmTuple;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
|
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmCteJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
|
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
|
@ -195,6 +203,8 @@ import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
|
@ -301,6 +311,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
private ParameterCollector parameterCollector;
|
private ParameterCollector parameterCollector;
|
||||||
private ParameterStyle parameterStyle;
|
private ParameterStyle parameterStyle;
|
||||||
|
|
||||||
|
private boolean isExtractingJdbcTemporalType;
|
||||||
|
// Provides access to the current CTE that is being processed, which is potentially recursive
|
||||||
|
// This is necessary, so that the recursive query part of a CTE can access its own structure.
|
||||||
|
// Note that the structure is based on the non-recursive query part, so there is no cycle
|
||||||
|
private JpaCteCriteria<?> currentPotentialRecursiveCte;
|
||||||
|
|
||||||
public SemanticQueryBuilder(
|
public SemanticQueryBuilder(
|
||||||
Class<R> expectedResultType,
|
Class<R> expectedResultType,
|
||||||
SqmCreationOptions creationOptions,
|
SqmCreationOptions creationOptions,
|
||||||
|
@ -617,10 +633,284 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Query spec
|
// Query spec
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitWithClause(HqlParser.WithClauseContext ctx) {
|
||||||
|
if ( creationOptions.useStrictJpaCompliance() ) {
|
||||||
|
throw new StrictJpaComplianceViolation(
|
||||||
|
StrictJpaComplianceViolation.Type.CTES
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final List<ParseTree> children = ctx.children;
|
||||||
|
for ( int i = 1; i < children.size(); i += 2 ) {
|
||||||
|
visitCte( (HqlParser.CteContext) children.get( i ) );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCte(HqlParser.CteContext ctx) {
|
||||||
|
final SqmCteContainer cteContainer = (SqmCteContainer) processingStateStack.getCurrent().getProcessingQuery();
|
||||||
|
final String name = visitIdentifier( (HqlParser.IdentifierContext) ctx.children.get( 0 ) );
|
||||||
|
final TerminalNode thirdChild = (TerminalNode) ctx.getChild( 2 );
|
||||||
|
final int queryExpressionIndex;
|
||||||
|
final CteMaterialization materialization;
|
||||||
|
switch ( thirdChild.getSymbol().getType() ) {
|
||||||
|
case HqlParser.NOT:
|
||||||
|
materialization = CteMaterialization.NOT_MATERIALIZED;
|
||||||
|
queryExpressionIndex = 5;
|
||||||
|
break;
|
||||||
|
case HqlParser.MATERIALIZED:
|
||||||
|
materialization = CteMaterialization.MATERIALIZED;
|
||||||
|
queryExpressionIndex = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
materialization = null;
|
||||||
|
queryExpressionIndex = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HqlParser.QueryExpressionContext queryExpressionContext = (HqlParser.QueryExpressionContext) ctx.getChild( queryExpressionIndex );
|
||||||
|
final SqmSelectQuery<Object> cte;
|
||||||
|
if ( cteContainer instanceof SqmSubQuery<?> ) {
|
||||||
|
cte = new SqmSubQuery<>(
|
||||||
|
processingStateStack.getCurrent().getProcessingQuery(),
|
||||||
|
creationContext.getNodeBuilder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cte = new SqmSelectStatement<>( creationContext.getNodeBuilder() );
|
||||||
|
}
|
||||||
|
processingStateStack.push(
|
||||||
|
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||||
|
processingStateStack.getCurrent(),
|
||||||
|
cte,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final JpaCteCriteria<?> oldCte = currentPotentialRecursiveCte;
|
||||||
|
try {
|
||||||
|
currentPotentialRecursiveCte = null;
|
||||||
|
if ( queryExpressionContext instanceof HqlParser.SetQueryGroupContext ) {
|
||||||
|
final HqlParser.SetQueryGroupContext setContext = (HqlParser.SetQueryGroupContext) queryExpressionContext;
|
||||||
|
// A recursive query is only possible if the child count is lower than 5 e.g. `withClause? q1 op q2`
|
||||||
|
if ( setContext.getChildCount() < 5 ) {
|
||||||
|
final SetOperator setOperator = (SetOperator) setContext.getChild( setContext.getChildCount() - 2 )
|
||||||
|
.accept( this );
|
||||||
|
switch ( setOperator ) {
|
||||||
|
case UNION:
|
||||||
|
case UNION_ALL:
|
||||||
|
final HqlParser.OrderedQueryContext nonRecursiveQueryContext;
|
||||||
|
final HqlParser.OrderedQueryContext recursiveQueryContext;
|
||||||
|
// On count == 4, we have a withClause at index 0
|
||||||
|
if ( setContext.getChildCount() == 4 ) {
|
||||||
|
nonRecursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 1 );
|
||||||
|
recursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 3 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nonRecursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 0 );
|
||||||
|
recursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 2 );
|
||||||
|
}
|
||||||
|
// First visit the non-recursive part
|
||||||
|
nonRecursiveQueryContext.accept( this );
|
||||||
|
|
||||||
|
// Visiting the possibly recursive part must happen within the call to SqmCteContainer.with,
|
||||||
|
// because in there, the SqmCteStatement/JpaCteCriteria is available for use in the recursive part.
|
||||||
|
// The structure (SqmCteTable) for the SqmCteStatement is based on the non-recursive part,
|
||||||
|
// which is necessary to have, so that the SqmCteRoot/SqmCteJoin can resolve sub-paths.
|
||||||
|
final SqmSelectStatement<Object> recursivePart = new SqmSelectStatement<>( creationContext.getNodeBuilder() );
|
||||||
|
|
||||||
|
processingStateStack.pop();
|
||||||
|
processingStateStack.push(
|
||||||
|
new SqmQueryPartCreationProcessingStateStandardImpl(
|
||||||
|
processingStateStack.getCurrent(),
|
||||||
|
recursivePart,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final JpaCteCriteria<Object> cteDefinition;
|
||||||
|
if ( setOperator == SetOperator.UNION ) {
|
||||||
|
cteDefinition = cteContainer.withRecursiveUnionDistinct(
|
||||||
|
name,
|
||||||
|
cte,
|
||||||
|
cteCriteria -> {
|
||||||
|
currentPotentialRecursiveCte = cteCriteria;
|
||||||
|
recursiveQueryContext.accept( this );
|
||||||
|
return recursivePart;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cteDefinition = cteContainer.withRecursiveUnionAll(
|
||||||
|
name,
|
||||||
|
cte,
|
||||||
|
cteCriteria -> {
|
||||||
|
currentPotentialRecursiveCte = cteCriteria;
|
||||||
|
recursiveQueryContext.accept( this );
|
||||||
|
return recursivePart;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( materialization != null ) {
|
||||||
|
cteDefinition.setMaterialization( materialization );
|
||||||
|
}
|
||||||
|
final ParseTree lastChild = ctx.getChild( ctx.getChildCount() - 1 );
|
||||||
|
final ParseTree potentialSearchClause;
|
||||||
|
if ( lastChild instanceof HqlParser.CycleClauseContext ) {
|
||||||
|
applyCycleClause( cteDefinition, (HqlParser.CycleClauseContext) lastChild );
|
||||||
|
potentialSearchClause = ctx.getChild( ctx.getChildCount() - 2 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
potentialSearchClause = lastChild;
|
||||||
|
}
|
||||||
|
if ( potentialSearchClause instanceof HqlParser.SearchClauseContext ) {
|
||||||
|
applySearchClause( cteDefinition, (HqlParser.SearchClauseContext) potentialSearchClause );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryExpressionContext.accept( this );
|
||||||
|
final JpaCteCriteria<Object> cteDefinition = cteContainer.with( name, cte );
|
||||||
|
if ( materialization != null ) {
|
||||||
|
cteDefinition.setMaterialization( materialization );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
processingStateStack.pop();
|
||||||
|
currentPotentialRecursiveCte = oldCte;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyCycleClause(JpaCteCriteria<?> cteDefinition, HqlParser.CycleClauseContext ctx) {
|
||||||
|
final HqlParser.CteAttributesContext attributesContext = (HqlParser.CteAttributesContext) ctx.getChild( 1 );
|
||||||
|
final String cycleMarkAttributeName = visitIdentifier( (HqlParser.IdentifierContext) ctx.getChild( 3 ) );
|
||||||
|
final List<JpaCteCriteriaAttribute> cycleAttributes = new ArrayList<>( ( attributesContext.getChildCount() + 1 ) >> 1 );
|
||||||
|
final List<ParseTree> children = attributesContext.children;
|
||||||
|
final JpaCteCriteriaType<?> type = cteDefinition.getType();
|
||||||
|
for ( int i = 0; i < children.size(); i += 2 ) {
|
||||||
|
final String attributeName = visitIdentifier( (HqlParser.IdentifierContext) children.get( i ) );
|
||||||
|
final JpaCteCriteriaAttribute attribute = type.getAttribute( attributeName );
|
||||||
|
if ( attribute == null ) {
|
||||||
|
throw new SemanticException(
|
||||||
|
String.format(
|
||||||
|
"Cycle attribute '%s' not found in the CTE %s",
|
||||||
|
attributeName,
|
||||||
|
cteDefinition.getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cycleAttributes.add( attribute );
|
||||||
|
}
|
||||||
|
|
||||||
|
final String cyclePathAttributeName;
|
||||||
|
final Object cycleValue;
|
||||||
|
final Object noCycleValue;
|
||||||
|
if ( ctx.getChildCount() > 4 ) {
|
||||||
|
if ( ctx.getChildCount() > 6 ) {
|
||||||
|
final SqmLiteral<?> cycleLiteral = (SqmLiteral<?>) visitLiteral( (HqlParser.LiteralContext) ctx.getChild( 5 ) );
|
||||||
|
final SqmLiteral<?> noCycleLiteral = (SqmLiteral<?>) visitLiteral( (HqlParser.LiteralContext) ctx.getChild( 7 ) );
|
||||||
|
cycleValue = cycleLiteral.getLiteralValue();
|
||||||
|
noCycleValue = noCycleLiteral.getLiteralValue();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cycleValue = Boolean.TRUE;
|
||||||
|
noCycleValue = Boolean.FALSE;
|
||||||
|
}
|
||||||
|
final ParseTree lastChild = ctx.getChild( ctx.getChildCount() - 1 );
|
||||||
|
if ( lastChild instanceof HqlParser.IdentifierContext ) {
|
||||||
|
cyclePathAttributeName = visitIdentifier( (HqlParser.IdentifierContext) lastChild );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cyclePathAttributeName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cyclePathAttributeName = null;
|
||||||
|
cycleValue = Boolean.TRUE;
|
||||||
|
noCycleValue = Boolean.FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cteDefinition.cycleUsing( cycleMarkAttributeName, cyclePathAttributeName, cycleValue, noCycleValue, cycleAttributes );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySearchClause(JpaCteCriteria<?> cteDefinition, HqlParser.SearchClauseContext ctx) {
|
||||||
|
final CteSearchClauseKind kind;
|
||||||
|
if ( ( (TerminalNode) ctx.getChild( 1 ) ).getSymbol().getType() == HqlParser.BREADTH ) {
|
||||||
|
kind = CteSearchClauseKind.BREADTH_FIRST;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kind = CteSearchClauseKind.DEPTH_FIRST;
|
||||||
|
}
|
||||||
|
final String searchAttributeName = visitIdentifier( (HqlParser.IdentifierContext) ctx.getChild( ctx.getChildCount() - 1 ) );
|
||||||
|
final HqlParser.SearchSpecificationsContext searchCtx = (HqlParser.SearchSpecificationsContext) ctx.getChild( 4 );
|
||||||
|
final List<JpaSearchOrder> searchOrders = new ArrayList<>( ( searchCtx.getChildCount() + 1 ) >> 1 );;
|
||||||
|
final List<ParseTree> children = searchCtx.children;
|
||||||
|
final JpaCteCriteriaType<?> type = cteDefinition.getType();
|
||||||
|
for ( int i = 0; i < children.size(); i += 2 ) {
|
||||||
|
final HqlParser.SearchSpecificationContext specCtx = (HqlParser.SearchSpecificationContext) children.get( i );
|
||||||
|
final String attributeName = visitIdentifier( (HqlParser.IdentifierContext) specCtx.getChild( 0 ) );
|
||||||
|
final JpaCteCriteriaAttribute attribute = type.getAttribute( attributeName );
|
||||||
|
if ( attribute == null ) {
|
||||||
|
throw new SemanticException(
|
||||||
|
String.format(
|
||||||
|
"Search attribute '%s' not found in the CTE %s",
|
||||||
|
attributeName,
|
||||||
|
cteDefinition.getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SortOrder sortOrder = SortOrder.ASCENDING;
|
||||||
|
NullPrecedence nullPrecedence = NullPrecedence.NONE;
|
||||||
|
int index = 1;
|
||||||
|
if ( index < specCtx.getChildCount() ) {
|
||||||
|
if ( specCtx.getChild( index ) instanceof HqlParser.SortDirectionContext ) {
|
||||||
|
final HqlParser.SortDirectionContext sortCtx = (HqlParser.SortDirectionContext) specCtx.getChild( index );
|
||||||
|
switch ( ( (TerminalNode) sortCtx.getChild( 0 ) ).getSymbol().getType() ) {
|
||||||
|
case HqlParser.ASC:
|
||||||
|
sortOrder = SortOrder.ASCENDING;
|
||||||
|
break;
|
||||||
|
case HqlParser.DESC:
|
||||||
|
sortOrder = SortOrder.DESCENDING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SemanticException( "Unrecognized sort ordering: " + sortCtx.getText() );
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if ( index < specCtx.getChildCount() ) {
|
||||||
|
final HqlParser.NullsPrecedenceContext nullsPrecedenceContext = (HqlParser.NullsPrecedenceContext) specCtx.getChild( index );
|
||||||
|
switch ( ( (TerminalNode) nullsPrecedenceContext.getChild( 1 ) ).getSymbol().getType() ) {
|
||||||
|
case HqlParser.FIRST:
|
||||||
|
nullPrecedence = NullPrecedence.FIRST;
|
||||||
|
break;
|
||||||
|
case HqlParser.LAST:
|
||||||
|
nullPrecedence = NullPrecedence.LAST;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SemanticException( "Unrecognized null precedence: " + nullsPrecedenceContext.getText() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchOrders.add(
|
||||||
|
creationContext.getNodeBuilder().search(
|
||||||
|
attribute,
|
||||||
|
sortOrder,
|
||||||
|
nullPrecedence
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cteDefinition.search( kind, searchAttributeName, searchOrders );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQueryPart<Object> visitSimpleQueryGroup(HqlParser.SimpleQueryGroupContext ctx) {
|
public SqmQueryPart<Object> visitSimpleQueryGroup(HqlParser.SimpleQueryGroupContext ctx) {
|
||||||
|
final int lastChild = ctx.getChildCount() - 1;
|
||||||
|
if ( lastChild != 0 ) {
|
||||||
|
ctx.getChild( 0 ).accept( this );
|
||||||
|
}
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (SqmQueryPart<Object>) ctx.getChild( 0 ).accept( this );
|
return (SqmQueryPart<Object>) ctx.getChild( lastChild ).accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -654,14 +944,22 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmQueryGroup<Object> visitSetQueryGroup(HqlParser.SetQueryGroupContext ctx) {
|
public SqmQueryGroup<Object> visitSetQueryGroup(HqlParser.SetQueryGroupContext ctx) {
|
||||||
|
final List<ParseTree> children = ctx.children;
|
||||||
|
final int firstIndex;
|
||||||
|
if ( children.get( 0 ) instanceof HqlParser.WithClauseContext ) {
|
||||||
|
children.get( 0 ).accept( this );
|
||||||
|
firstIndex = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
firstIndex = 0;
|
||||||
|
}
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
if ( creationOptions.useStrictJpaCompliance() ) {
|
||||||
throw new StrictJpaComplianceViolation(
|
throw new StrictJpaComplianceViolation(
|
||||||
StrictJpaComplianceViolation.Type.SET_OPERATIONS
|
StrictJpaComplianceViolation.Type.SET_OPERATIONS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final List<ParseTree> children = ctx.children;
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final SqmQueryPart<Object> firstQueryPart = (SqmQueryPart<Object>) children.get( 0 ).accept( this );
|
final SqmQueryPart<Object> firstQueryPart = (SqmQueryPart<Object>) children.get( firstIndex ).accept( this );
|
||||||
SqmQueryGroup<Object> queryGroup;
|
SqmQueryGroup<Object> queryGroup;
|
||||||
if ( firstQueryPart instanceof SqmQueryGroup<?>) {
|
if ( firstQueryPart instanceof SqmQueryGroup<?>) {
|
||||||
queryGroup = (SqmQueryGroup<Object>) firstQueryPart;
|
queryGroup = (SqmQueryGroup<Object>) firstQueryPart;
|
||||||
|
@ -672,7 +970,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
setCurrentQueryPart( queryGroup );
|
setCurrentQueryPart( queryGroup );
|
||||||
final int size = children.size();
|
final int size = children.size();
|
||||||
final SqmCreationProcessingState firstProcessingState = processingStateStack.pop();
|
final SqmCreationProcessingState firstProcessingState = processingStateStack.pop();
|
||||||
for ( int i = 1; i < size; i += 2 ) {
|
for ( int i = firstIndex + 1; i < size; i += 2 ) {
|
||||||
final SetOperator operator = visitSetOperator( (HqlParser.SetOperatorContext) children.get( i ) );
|
final SetOperator operator = visitSetOperator( (HqlParser.SetOperatorContext) children.get( i ) );
|
||||||
final HqlParser.OrderedQueryContext simpleQueryCtx =
|
final HqlParser.OrderedQueryContext simpleQueryCtx =
|
||||||
(HqlParser.OrderedQueryContext) children.get( i + 1 );
|
(HqlParser.OrderedQueryContext) children.get( i + 1 );
|
||||||
|
@ -1625,6 +1923,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'" );
|
throw new SemanticException( "Could not resolve entity or correlation path '" + name + "'" );
|
||||||
}
|
}
|
||||||
|
final SqmCteStatement<?> cteStatement = findCteStatement( name );
|
||||||
|
if ( cteStatement != null ) {
|
||||||
|
final SqmCteRoot<?> root = new SqmCteRoot<>( cteStatement, alias );
|
||||||
|
pathRegistry.register( root );
|
||||||
|
return root;
|
||||||
|
}
|
||||||
throw new UnknownEntityException( "Could not resolve root entity '" + name + "'", name );
|
throw new UnknownEntityException( "Could not resolve root entity '" + name + "'", name );
|
||||||
}
|
}
|
||||||
checkFQNEntityNameJpaComplianceViolationIfNeeded( name, entityDescriptor );
|
checkFQNEntityNameJpaComplianceViolationIfNeeded( name, entityDescriptor );
|
||||||
|
@ -1652,6 +1956,22 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return sqmRoot;
|
return sqmRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmCteStatement<?> findCteStatement(String name) {
|
||||||
|
if ( currentPotentialRecursiveCte != null && name.equals( currentPotentialRecursiveCte.getName() ) ) {
|
||||||
|
return (SqmCteStatement<?>) currentPotentialRecursiveCte;
|
||||||
|
}
|
||||||
|
return processingStateStack.findCurrentFirst(
|
||||||
|
state -> {
|
||||||
|
if ( state.getProcessingQuery() instanceof SqmCteContainer ) {
|
||||||
|
final SqmCteContainer container = (SqmCteContainer) state.getProcessingQuery();
|
||||||
|
return container.getCteStatement( name );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmRoot<?> visitRootSubquery(HqlParser.RootSubqueryContext ctx) {
|
public SqmRoot<?> visitRootSubquery(HqlParser.RootSubqueryContext ctx) {
|
||||||
if ( getCreationOptions().useStrictJpaCompliance() ) {
|
if ( getCreationOptions().useStrictJpaCompliance() ) {
|
||||||
|
@ -1867,7 +2187,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
final HqlParser.JoinRestrictionContext qualifiedJoinRestrictionContext = parserJoin.joinRestriction();
|
final HqlParser.JoinRestrictionContext qualifiedJoinRestrictionContext = parserJoin.joinRestriction();
|
||||||
if ( join instanceof SqmEntityJoin<?> || join instanceof SqmDerivedJoin<?> ) {
|
if ( join instanceof SqmEntityJoin<?> || join instanceof SqmDerivedJoin<?> || join instanceof SqmCteJoin<?> ) {
|
||||||
sqmRoot.addSqmJoin( join );
|
sqmRoot.addSqmJoin( join );
|
||||||
}
|
}
|
||||||
else if ( join instanceof SqmAttributeJoin<?, ?> ) {
|
else if ( join instanceof SqmAttributeJoin<?, ?> ) {
|
||||||
|
@ -2165,7 +2485,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final Enum<?> enumValue;
|
final Enum<?> enumValue;
|
||||||
if ( possibleEnumValues != null && ( enumValue = possibleEnumValues.get( enumType ) ) != null ) {
|
if ( possibleEnumValues != null && ( enumValue = possibleEnumValues.get( enumType ) ) != null ) {
|
||||||
DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
|
DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
|
||||||
dotIdentifierConsumer.consumeIdentifier( enumValue.getClass().getCanonicalName(), true, false );
|
dotIdentifierConsumer.consumeIdentifier( enumValue.getClass().getName(), true, false );
|
||||||
dotIdentifierConsumer.consumeIdentifier( enumValue.name(), false, true );
|
dotIdentifierConsumer.consumeIdentifier( enumValue.name(), false, true );
|
||||||
return (SqmExpression<?>) dotIdentifierConsumerStack.getCurrent().getConsumedPart();
|
return (SqmExpression<?>) dotIdentifierConsumerStack.getCurrent().getConsumedPart();
|
||||||
}
|
}
|
||||||
|
@ -3940,8 +4260,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExtractingJdbcTemporalType;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitExtractFunction(HqlParser.ExtractFunctionContext ctx) {
|
public Object visitExtractFunction(HqlParser.ExtractFunctionContext ctx) {
|
||||||
final SqmExpression<?> expressionToExtract = (SqmExpression<?>) ctx.getChild( ctx.getChildCount() - 2 )
|
final SqmExpression<?> expressionToExtract = (SqmExpression<?>) ctx.getChild( ctx.getChildCount() - 2 )
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.query.hql.spi;
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models the state pertaining to the creation of a single SQM.
|
* Models the state pertaining to the creation of a single SQM.
|
||||||
|
@ -39,4 +40,6 @@ public interface SqmCreationState {
|
||||||
default SqmCreationProcessingState getCurrentProcessingState() {
|
default SqmCreationProcessingState getCurrentProcessingState() {
|
||||||
return getProcessingStateStack().getCurrent();
|
return getProcessingStateStack().getCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SqmCteStatement<?> findCteStatement(String name);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue