Skip to content

Commit 8ece1d0

Browse files
committed
HHH-19240 Left-factor CONTAINS/INCLUDES/INTERSECTS to single BinaryExpressionPredicate rule
1 parent 0e26a24 commit 8ece1d0

File tree

2 files changed

+87
-76
lines changed

2 files changed

+87
-76
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
@@ -660,9 +660,7 @@ predicate
660660
| expression NOT? IN inList # InPredicate
661661
| expression NOT? BETWEEN expression AND expression # BetweenPredicate
662662
| expression NOT? (LIKE | ILIKE) REGEXP? expression likeEscape? # LikePredicate
663-
| expression NOT? CONTAINS expression # ContainsPredicate
664-
| expression NOT? INCLUDES expression # IncludesPredicate
665-
| expression NOT? INTERSECTS expression # IntersectsPredicate
663+
| expression NOT? (CONTAINS | INCLUDES | INTERSECTS) expression # BinaryExpressionPredicate
666664
| expression comparisonOperator expression # ComparisonPredicate
667665
| EXISTS collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
668666
| EXISTS expression # ExistsPredicate

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

Lines changed: 86 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.Set;
3131

3232
import jakarta.persistence.criteria.Nulls;
33+
import org.antlr.v4.runtime.Token;
3334
import org.hibernate.AssertionFailure;
3435
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
3536
import org.hibernate.cfg.QuerySettings;
@@ -2425,6 +2426,91 @@ public SqmPredicate visitUnaryIsPredicate(HqlParser.UnaryIsPredicateContext ctx)
24252426
};
24262427
}
24272428

2429+
@Override
2430+
public SqmPredicate visitBinaryExpressionPredicate(HqlParser.BinaryExpressionPredicateContext ctx) {
2431+
final var firstSymbol = ((TerminalNode) ctx.getChild( 1 )).getSymbol();
2432+
final boolean negated;
2433+
final Token operationSymbol;
2434+
if ( firstSymbol.getType() == HqlParser.NOT ) {
2435+
negated = true;
2436+
operationSymbol = ((TerminalNode) ctx.getChild( 2 )).getSymbol();
2437+
}
2438+
else {
2439+
negated = false;
2440+
operationSymbol = firstSymbol;
2441+
}
2442+
final var expressions = ctx.expression();
2443+
final var lhsCtx = expressions.get( 0 );
2444+
final var rhsCtx = expressions.get( 1 );
2445+
return switch ( operationSymbol.getType() ) {
2446+
case HqlParser.CONTAINS -> {
2447+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2448+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2449+
final var lhsExpressible = lhs.getExpressible();
2450+
if ( lhsExpressible != null && !(lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2451+
throw new SemanticException(
2452+
"First operand for contains predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2453+
query
2454+
);
2455+
}
2456+
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor(
2457+
"array_contains" ).generateSqmExpression(
2458+
asList( lhs, rhs ),
2459+
null,
2460+
queryEngine()
2461+
);
2462+
yield new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
2463+
}
2464+
case HqlParser.INCLUDES -> {
2465+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2466+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2467+
final var lhsExpressible = lhs.getExpressible();
2468+
final var rhsExpressible = rhs.getExpressible();
2469+
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2470+
throw new SemanticException(
2471+
"First operand for includes predicate must be a basic plural type expression, but found: "
2472+
+ lhsExpressible.getSqmType(),
2473+
query
2474+
);
2475+
}
2476+
if ( rhsExpressible != null && !( rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2477+
throw new SemanticException(
2478+
"Second operand for includes predicate must be a basic plural type expression, but found: "
2479+
+ rhsExpressible.getSqmType(),
2480+
query
2481+
);
2482+
}
2483+
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_includes" ).generateSqmExpression(
2484+
asList( lhs, rhs ),
2485+
null,
2486+
queryEngine()
2487+
);
2488+
yield new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
2489+
}
2490+
case HqlParser.INTERSECTS -> {
2491+
final var lhs = (SqmExpression<?>) lhsCtx.accept( this );
2492+
final var rhs = (SqmExpression<?>) rhsCtx.accept( this );
2493+
final var lhsExpressible = lhs.getExpressible();
2494+
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
2495+
throw new SemanticException(
2496+
"First operand for intersects predicate must be a basic plural type expression, but found: "
2497+
+ lhsExpressible.getSqmType(),
2498+
query
2499+
);
2500+
}
2501+
final SelfRenderingSqmFunction<Boolean> contains =
2502+
getFunctionDescriptor( "array_intersects" )
2503+
.generateSqmExpression(
2504+
asList( lhs, rhs ),
2505+
null,
2506+
queryEngine()
2507+
);
2508+
yield new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
2509+
}
2510+
default -> throw new AssertionError( "Unknown binary expression predicate: " + operationSymbol );
2511+
};
2512+
}
2513+
24282514
@Override
24292515
public Object visitComparisonOperator(HqlParser.ComparisonOperatorContext ctx) {
24302516
final var firstToken = (TerminalNode) ctx.getChild( 0 );
@@ -2573,27 +2659,6 @@ private String getPossibleEnumValue(HqlParser.ExpressionContext expressionContex
25732659
return null;
25742660
}
25752661

2576-
@Override
2577-
public SqmPredicate visitContainsPredicate(HqlParser.ContainsPredicateContext ctx) {
2578-
final boolean negated = ctx.NOT() != null;
2579-
final var lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
2580-
final var rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
2581-
final var lhsExpressible = lhs.getExpressible();
2582-
if ( lhsExpressible != null
2583-
&& !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?> ) ) {
2584-
throw new SemanticException(
2585-
"First operand for contains predicate must be a basic plural type expression, but found: " + lhsExpressible.getSqmType(),
2586-
query
2587-
);
2588-
}
2589-
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_contains" ).generateSqmExpression(
2590-
asList( lhs, rhs ),
2591-
null,
2592-
queryEngine()
2593-
);
2594-
return new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
2595-
}
2596-
25972662
@Override
25982663
public SqmExpression<?> visitJsonValueFunction(HqlParser.JsonValueFunctionContext ctx) {
25992664
checkJsonFunctionsEnabled( ctx );
@@ -3128,58 +3193,6 @@ private void checkXmlFunctionsEnabled(ParserRuleContext ctx) {
31283193
}
31293194
}
31303195

3131-
@Override
3132-
public SqmPredicate visitIncludesPredicate(HqlParser.IncludesPredicateContext ctx) {
3133-
final boolean negated = ctx.NOT() != null;
3134-
final var lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
3135-
final var rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
3136-
final var lhsExpressible = lhs.getExpressible();
3137-
final var rhsExpressible = rhs.getExpressible();
3138-
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
3139-
throw new SemanticException(
3140-
"First operand for includes predicate must be a basic plural type expression, but found: "
3141-
+ lhsExpressible.getSqmType(),
3142-
query
3143-
);
3144-
}
3145-
if ( rhsExpressible != null && !( rhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
3146-
throw new SemanticException(
3147-
"Second operand for includes predicate must be a basic plural type expression, but found: "
3148-
+ rhsExpressible.getSqmType(),
3149-
query
3150-
);
3151-
}
3152-
final SelfRenderingSqmFunction<Boolean> contains = getFunctionDescriptor( "array_includes" ).generateSqmExpression(
3153-
asList( lhs, rhs ),
3154-
null,
3155-
queryEngine()
3156-
);
3157-
return new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
3158-
}
3159-
3160-
@Override
3161-
public SqmPredicate visitIntersectsPredicate(HqlParser.IntersectsPredicateContext ctx) {
3162-
final boolean negated = ctx.NOT() != null;
3163-
final var lhs = (SqmExpression<?>) ctx.expression( 0 ).accept( this );
3164-
final var rhs = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
3165-
final var lhsExpressible = lhs.getExpressible();
3166-
if ( lhsExpressible != null && !( lhsExpressible.getSqmType() instanceof BasicPluralType<?, ?>) ) {
3167-
throw new SemanticException(
3168-
"First operand for intersects predicate must be a basic plural type expression, but found: "
3169-
+ lhsExpressible.getSqmType(),
3170-
query
3171-
);
3172-
}
3173-
final SelfRenderingSqmFunction<Boolean> contains =
3174-
getFunctionDescriptor( "array_intersects" )
3175-
.generateSqmExpression(
3176-
asList( lhs, rhs ),
3177-
null,
3178-
queryEngine()
3179-
);
3180-
return new SqmBooleanExpressionPredicate( contains, negated, nodeBuilder() );
3181-
}
3182-
31833196
@Override
31843197
public SqmPredicate visitLikePredicate(HqlParser.LikePredicateContext ctx) {
31853198
final boolean negated = ctx.NOT() != null;

0 commit comments

Comments
 (0)