Skip to content

Commit 20cdfe0

Browse files
committed
HHH-19240 Left-factor CONTAINS/INCLUDES/INTERSECTS to single BinaryExpressionPredicate rule
(cherry picked from commit 1eecda8)
1 parent 22af945 commit 20cdfe0

File tree

2 files changed

+88
-70
lines changed

2 files changed

+88
-70
lines changed

hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,7 @@ predicate
663663
| expression NOT? IN inList # InPredicate
664664
| expression NOT? BETWEEN expression AND expression # BetweenPredicate
665665
| expression NOT? (LIKE | ILIKE) expression likeEscape? # LikePredicate
666-
| expression NOT? CONTAINS expression # ContainsPredicate
667-
| expression NOT? INCLUDES expression # IncludesPredicate
668-
| expression NOT? INTERSECTS expression # IntersectsPredicate
666+
| expression NOT? (CONTAINS | INCLUDES | INTERSECTS) expression # BinaryExpressionPredicate
669667
| expression comparisonOperator expression # ComparisonPredicate
670668
| EXISTS collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
671669
| EXISTS expression # ExistsPredicate

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 87 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.Map;
3434
import java.util.Set;
3535

36+
import org.antlr.v4.runtime.Token;
3637
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
3738
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
3839
import org.hibernate.dialect.function.SqlColumn;
@@ -2477,6 +2478,92 @@ public SqmPredicate visitUnaryIsPredicate(HqlParser.UnaryIsPredicateContext ctx)
24772478
}
24782479
}
24792480

2481+
@Override
2482+
public SqmPredicate visitBinaryExpressionPredicate(HqlParser.BinaryExpressionPredicateContext ctx) {
2483+
final var firstSymbol = ((TerminalNode) ctx.getChild( 1 )).getSymbol();
2484+
final boolean negated;
2485+
final Token operationSymbol;
2486+
if ( firstSymbol.getType() == HqlParser.NOT ) {
2487+
negated = true;
2488+
operationSymbol = ((TerminalNode) ctx.getChild( 2 )).getSymbol();
2489+
}
2490+
else {
2491+
negated = false;
2492+
operationSymbol = firstSymbol;
2493+
}
2494+
final var expressions = ctx.expression();
2495+
final var lhsCtx = expressions.get( 0 );
2496+
final var rhsCtx = expressions.get( 1 );
2497+
switch ( operationSymbol.getType() ) {
2498+
case HqlParser.CONTAINS: {
2499+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2500+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2501+
final var lhsExpressible = lhs.getExpressible();
2502+
if ( lhsExpressible != null && !(lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2503+
throw new SemanticException(
2504+
"First operand for contains predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2505+
query
2506+
);
2507+
}
2508+
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor(
2509+
"array_contains" ).generateSqmExpression(
2510+
asList( lhs, rhs ),
2511+
null,
2512+
creationContext.getQueryEngine()
2513+
);
2514+
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2515+
}
2516+
case HqlParser.INCLUDES: {
2517+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2518+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2519+
final var lhsExpressible = lhs.getExpressible();
2520+
final var rhsExpressible = rhs.getExpressible();
2521+
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2522+
throw new SemanticException(
2523+
"First operand for includes predicate must be a basic plural type expression, but found: "
2524+
+ lhsExpressible.getSqmType(),
2525+
query
2526+
);
2527+
}
2528+
if ( rhsExpressible != null && !( rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2529+
throw new SemanticException(
2530+
"Second operand for includes predicate must be a basic plural type expression, but found: "
2531+
+ rhsExpressible.getSqmType(),
2532+
query
2533+
);
2534+
}
2535+
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_includes" ).generateSqmExpression(
2536+
asList( lhs, rhs ),
2537+
null,
2538+
creationContext.getQueryEngine()
2539+
);
2540+
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2541+
}
2542+
case HqlParser.INTERSECTS: {
2543+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2544+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2545+
final var lhsExpressible = lhs.getExpressible();
2546+
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?> ) ) {
2547+
throw new SemanticException(
2548+
"First operand for intersects predicate must be a basic plural type expression, but found: "
2549+
+ lhsExpressible.getSqmType(),
2550+
query
2551+
);
2552+
}
2553+
final SelfRenderingSqmFunction<Boolean> contains =
2554+
getFunctionDescriptor( "array_intersects" )
2555+
.generateSqmExpression(
2556+
asList( lhs, rhs ),
2557+
null,
2558+
creationContext.getQueryEngine()
2559+
);
2560+
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2561+
}
2562+
default:
2563+
throw new AssertionError( "Unknown binary expression predicate: " + operationSymbol );
2564+
}
2565+
}
2566+
24802567
@Override
24812568
public Object visitComparisonOperator(HqlParser.ComparisonOperatorContext ctx) {
24822569
final TerminalNode firstToken = (TerminalNode) ctx.getChild( 0 );
@@ -2642,73 +2729,6 @@ private String getPossibleEnumValue(HqlParser.ExpressionContext expressionContex
26422729
return null;
26432730
}
26442731

2645-
@Override
2646-
public SqmPredicate visitContainsPredicate(HqlParser.ContainsPredicateContext ctx) {
2647-
final boolean negated = ctx.NOT() != null;
2648-
final SqmExpression<?> lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
2649-
final SqmExpression<?> rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
2650-
final SqmExpressible<?> lhsExpressible = lhs.getExpressible();
2651-
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2652-
throw new SemanticException(
2653-
"First operand for contains predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2654-
query
2655-
);
2656-
}
2657-
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_contains" ).generateSqmExpression(
2658-
asList( lhs, rhs ),
2659-
null,
2660-
creationContext.getQueryEngine()
2661-
);
2662-
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2663-
}
2664-
2665-
@Override
2666-
public SqmPredicate visitIncludesPredicate(HqlParser.IncludesPredicateContext ctx) {
2667-
final boolean negated = ctx.NOT() != null;
2668-
final SqmExpression<?> lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
2669-
final SqmExpression<?> rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
2670-
final SqmExpressible<?> lhsExpressible = lhs.getExpressible();
2671-
final SqmExpressible<?> rhsExpressible = rhs.getExpressible();
2672-
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2673-
throw new SemanticException(
2674-
"First operand for includes predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2675-
query
2676-
);
2677-
}
2678-
if ( rhsExpressible != null && !( rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2679-
throw new SemanticException(
2680-
"Second operand for includes predicate must be a basic plural type expression, but found: " + rhsExpressible.getSqmType(),
2681-
query
2682-
);
2683-
}
2684-
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_includes" ).generateSqmExpression(
2685-
asList( lhs, rhs ),
2686-
null,
2687-
creationContext.getQueryEngine()
2688-
);
2689-
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2690-
}
2691-
2692-
@Override
2693-
public SqmPredicate visitIntersectsPredicate(HqlParser.IntersectsPredicateContext ctx) {
2694-
final boolean negated = ctx.NOT() != null;
2695-
final SqmExpression<?> lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
2696-
final SqmExpression<?> rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
2697-
final SqmExpressible<?> lhsExpressible = lhs.getExpressible();
2698-
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2699-
throw new SemanticException(
2700-
"First operand for intersects predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2701-
query
2702-
);
2703-
}
2704-
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_intersects" ).generateSqmExpression(
2705-
asList( lhs, rhs ),
2706-
null,
2707-
creationContext.getQueryEngine()
2708-
);
2709-
return new SqmBooleanExpressionPredicate( contains, negated, creationContext.getNodeBuilder() );
2710-
}
2711-
27122732
@Override
27132733
public SqmPredicate visitLikePredicate(HqlParser.LikePredicateContext ctx) {
27142734
final boolean negated = ctx.NOT() != null;

0 commit comments

Comments
 (0)