From 472d8eaa25d8081aac008fe020ae834ed95c9991 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 26 Apr 2024 15:49:30 +0200 Subject: [PATCH] HHH-17982 Implement some JPA 3.2 methods --- .../hql/internal/SemanticQueryBuilder.java | 61 ++++------ .../sqm/internal/SqmCriteriaNodeBuilder.java | 115 +++++++++++++++--- .../query/sqm/tree/select/SqmQuerySpec.java | 13 +- 3 files changed, 135 insertions(+), 54 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 0a68e270d9..48c85b89f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -276,39 +276,37 @@ import static org.hibernate.type.spi.TypeConfiguration.isJdbcTemporalType; public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCreationState { private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class ); - private static final Set JPA_STANDARD_FUNCTIONS; + private static final Set JPA_STANDARD_FUNCTIONS = Set.of( + "avg", + "max", + "min", + "sum", + "count", + "length", + "locate", + "abs", + "sqrt", + "mod", + "size", + "index", + "current_date", + "current_time", + "current_timestamp", + "concat", + "substring", + "trim", + "lower", + "upper", + "coalesce", + "nullif", + "left", + "right", + "replace" + ); private static final BasicTypeImpl OBJECT_BASIC_TYPE = new BasicTypeImpl<>( new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE ); - static { - final Set jpaStandardFunctions = new HashSet<>(); - // Extracted from the BNF in JPA spec 4.14. - jpaStandardFunctions.add( "avg" ); - jpaStandardFunctions.add( "max" ); - jpaStandardFunctions.add( "min" ); - jpaStandardFunctions.add( "sum" ); - jpaStandardFunctions.add( "count" ); - jpaStandardFunctions.add( "length" ); - jpaStandardFunctions.add( "locate" ); - jpaStandardFunctions.add( "abs" ); - jpaStandardFunctions.add( "sqrt" ); - jpaStandardFunctions.add( "mod" ); - jpaStandardFunctions.add( "size" ); - jpaStandardFunctions.add( "index" ); - jpaStandardFunctions.add( "current_date" ); - jpaStandardFunctions.add( "current_time" ); - jpaStandardFunctions.add( "current_timestamp" ); - jpaStandardFunctions.add( "concat" ); - jpaStandardFunctions.add( "substring" ); - jpaStandardFunctions.add( "trim" ); - jpaStandardFunctions.add( "lower" ); - jpaStandardFunctions.add( "upper" ); - jpaStandardFunctions.add( "coalesce" ); - jpaStandardFunctions.add( "nullif" ); - JPA_STANDARD_FUNCTIONS = jpaStandardFunctions; - } - /** * Main entry point into analysis of HQL/JPQL parse tree - producing * a semantic model of the query. @@ -1073,11 +1071,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem else { firstIndex = 0; } - if ( creationOptions.useStrictJpaCompliance() ) { - throw new StrictJpaComplianceViolation( - StrictJpaComplianceViolation.Type.SET_OPERATIONS - ); - } final SqmQueryPart firstQueryPart = (SqmQueryPart) children.get( firstIndex ).accept( this ); SqmQueryGroup queryGroup; if ( firstQueryPart instanceof SqmQueryGroup) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 77cc56b5d8..a6b0c55e1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -427,7 +427,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, @Override public CriteriaSelect unionAll(CriteriaSelect left, CriteriaSelect right) { - return null; + if ( left instanceof Subquery ) { + assert right instanceof Subquery; + //noinspection unchecked + return setOperation( SetOperator.UNION_ALL, (Subquery) left, (Subquery) right ); + } + //noinspection unchecked + return setOperation( SetOperator.UNION_ALL, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override @@ -437,12 +443,24 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, @Override public CriteriaSelect except(CriteriaSelect left, CriteriaSelect right) { - return null; + if ( left instanceof Subquery ) { + assert right instanceof Subquery; + //noinspection unchecked + return setOperation( SetOperator.EXCEPT, (Subquery) left, (Subquery) right ); + } + //noinspection unchecked + return setOperation( SetOperator.EXCEPT, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override public CriteriaSelect exceptAll(CriteriaSelect left, CriteriaSelect right) { - return null; + if ( left instanceof Subquery ) { + assert right instanceof Subquery; + //noinspection unchecked + return setOperation( SetOperator.EXCEPT_ALL, (Subquery) left, (Subquery) right ); + } + //noinspection unchecked + return setOperation( SetOperator.EXCEPT_ALL, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override @@ -588,12 +606,24 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, @Override public CriteriaSelect intersect(CriteriaSelect left, CriteriaSelect right) { - return null; + if ( left instanceof Subquery ) { + assert right instanceof Subquery; + //noinspection unchecked + return setOperation( SetOperator.INTERSECT, (Subquery) left, (Subquery) right ); + } + //noinspection unchecked + return setOperation( SetOperator.INTERSECT, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override public CriteriaSelect intersectAll(CriteriaSelect left, CriteriaSelect right) { - return null; + if ( left instanceof Subquery ) { + assert right instanceof Subquery; + //noinspection unchecked + return setOperation( SetOperator.INTERSECT_ALL, (Subquery) left, (Subquery) right ); + } + //noinspection unchecked + return setOperation( SetOperator.INTERSECT_ALL, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -1614,6 +1644,16 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, return new JpaCriteriaParameter<>( name, parameterType, notBasic, this ); } + @Override + public SqmExpression concat(List> expressions) { + //noinspection unchecked + return getFunctionDescriptor( "concat" ).generateSqmExpression( + (List>) (List) expressions, + null, + getQueryEngine() + ); + } + @Override public SqmExpression concat(Expression x, Expression y) { final SqmExpression xSqmExpression = (SqmExpression) x; @@ -1918,11 +1958,6 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, ); } - @Override - public SqmExpression extract(TemporalField field, Expression temporal) { - return null; - } - @Override public SqmFunction function(String name, Class type, Expression[] args) { final BasicType resultType = getTypeConfiguration().standardBasicTypeForJavaType( type ); @@ -2244,7 +2279,15 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, @Override public SqmPredicate and(List restrictions) { - return null; + if ( restrictions == null || restrictions.isEmpty() ) { + return conjunction(); + } + + final List predicates = new ArrayList<>( restrictions.size() ); + for ( Predicate expression : restrictions ) { + predicates.add( (SqmPredicate) expression ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } @Override @@ -2805,11 +2848,6 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, return not( like( x, pattern, escapeChar ) ); } - @Override - public SqmExpression concat(List> expressions) { - return null; - } - @Override public SqmPredicate notIlike(Expression x, Expression pattern) { return not( ilike( x, pattern ) ); @@ -2957,6 +2995,51 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext, ); } + @Override + public SqmExpression extract(TemporalField field, Expression temporal) { + Class resultType = Integer.class; + final TemporalUnit temporalUnit; + switch ( field.toString() ) { + case "year": + temporalUnit = TemporalUnit.YEAR; + break; + case "quarter": + temporalUnit = TemporalUnit.QUARTER; + break; + case "month": + temporalUnit = TemporalUnit.MONTH; + break; + case "week": + temporalUnit = TemporalUnit.WEEK; + break; + case "day": + temporalUnit = TemporalUnit.DAY; + break; + case "hour": + temporalUnit = TemporalUnit.HOUR; + break; + case "minute": + temporalUnit = TemporalUnit.MINUTE; + break; + case "second": + temporalUnit = TemporalUnit.SECOND; + resultType = Double.class; + break; + case "date": + temporalUnit = TemporalUnit.DATE; + resultType = LocalDate.class; + break; + case "time": + temporalUnit = TemporalUnit.TIME; + resultType = LocalTime.class; + break; + default: + throw new IllegalArgumentException( "Invalid temporal field [" + field + "]" ); + } + //noinspection unchecked + return extract( temporal, temporalUnit, (Class) resultType ); + } + private SqmFunction extract( Expression datetime, TemporalUnit temporalUnit, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 724702ec79..512e4de37b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -355,11 +355,16 @@ public class SqmQuerySpec extends SqmQueryPart @Override public SqmQuerySpec setRestriction(Expression restriction) { - SqmWhereClause whereClause = getWhereClause(); - if ( whereClause == null ) { - setWhereClause( whereClause = new SqmWhereClause( nodeBuilder() ) ); + if ( restriction == null ) { + setWhereClause( null ); + } + else { + SqmWhereClause whereClause = getWhereClause(); + if ( whereClause == null ) { + setWhereClause( whereClause = new SqmWhereClause( nodeBuilder() ) ); + } + whereClause.setPredicate( nodeBuilder().wrap( restriction ) ); } - whereClause.setPredicate( nodeBuilder().wrap( restriction ) ); return this; }