HHH-15392 Infer Character as type for the LIKE predicate escape expression

This commit is contained in:
Christian Beikov 2022-07-21 13:00:23 +02:00
parent 02a1d1a27e
commit 38589b5e7b
5 changed files with 63 additions and 5 deletions

View File

@ -2181,7 +2181,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return new SqmLikePredicate(
(SqmExpression<?>) ctx.getChild( 0 ).accept( this ),
(SqmExpression<?>) ctx.getChild( startIndex ).accept( this ),
(SqmExpression<?>) ctx.getChild( startIndex + 1 ).getChild( 1 ).accept( this ),
(SqmExpression<?>) ctx.getChild( startIndex + 1 ).accept( this ),
negated,
caseSensitive,
creationContext.getNodeBuilder()
@ -2198,6 +2198,38 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
}
@Override
public Object visitLikeEscape(HqlParser.LikeEscapeContext ctx) {
final ParseTree child = ctx.getChild( 1 );
if ( child instanceof HqlParser.NamedParameterContext ) {
return visitNamedParameter(
(HqlParser.NamedParameterContext) child,
creationContext.getNodeBuilder().getCharacterType()
);
}
else if ( child instanceof HqlParser.PositionalParameterContext ) {
return visitPositionalParameter(
(HqlParser.PositionalParameterContext) child,
creationContext.getNodeBuilder().getCharacterType()
);
}
else {
assert child instanceof TerminalNode;
final TerminalNode terminalNode = (TerminalNode) child;
final String escape = QuotingHelper.unquoteStringLiteral( terminalNode.getText() );
if ( escape.length() != 1 ) {
throw new SemanticException(
"Escape character literals must have exactly a single character, but found: " + escape
);
}
return new SqmLiteral<>(
escape.charAt( 0 ),
creationContext.getNodeBuilder().getCharacterType(),
creationContext.getNodeBuilder()
);
}
}
@Override
public SqmPredicate visitMemberOfPredicate(HqlParser.MemberOfPredicateContext ctx) {
final boolean negated = ctx.NOT() != null;
@ -3398,10 +3430,17 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public SqmNamedParameter<?> visitNamedParameter(HqlParser.NamedParameterContext ctx) {
return visitNamedParameter( ctx, null );
}
private <T> SqmNamedParameter<T> visitNamedParameter(
HqlParser.NamedParameterContext ctx,
SqmExpressible<T> expressibleType) {
parameterStyle = parameterStyle.withNamed();
final SqmNamedParameter<?> param = new SqmNamedParameter<>(
final SqmNamedParameter<T> param = new SqmNamedParameter<>(
ctx.getChild( 1 ).getText(),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
expressibleType,
creationContext.getNodeBuilder()
);
parameterCollector.addParameter( param );
@ -3410,13 +3449,20 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public SqmPositionalParameter<?> visitPositionalParameter(HqlParser.PositionalParameterContext ctx) {
return visitPositionalParameter( ctx, null );
}
private <T> SqmPositionalParameter<T> visitPositionalParameter(
HqlParser.PositionalParameterContext ctx,
SqmExpressible<T> expressibleType) {
if ( ctx.getChildCount() == 1 ) {
throw new SemanticException( "Unlabeled ordinal parameter ('?' rather than ?1)" );
}
parameterStyle = parameterStyle.withPositional();
final SqmPositionalParameter<?> param = new SqmPositionalParameter<>(
final SqmPositionalParameter<T> param = new SqmPositionalParameter<>(
Integer.parseInt( ctx.getChild( 1 ).getText() ),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
expressibleType,
creationContext.getNodeBuilder()
);
parameterCollector.addParameter( param );

View File

@ -644,4 +644,6 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
BasicType<Boolean> getBooleanType();
BasicType<Integer> getIntegerType();
BasicType<Character> getCharacterType();
}

View File

@ -178,6 +178,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
private final transient ValueHandlingMode criteriaValueHandlingMode;
private transient BasicType<Boolean> booleanType;
private transient BasicType<Integer> integerType;
private transient BasicType<Character> characterType;
public SqmCriteriaNodeBuilder(
String uuid,
@ -225,6 +226,15 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return integerType;
}
@Override
public BasicType<Character> getCharacterType() {
final BasicType<Character> characterType = this.characterType;
if ( characterType == null ) {
return this.characterType = getTypeConfiguration().getBasicTypeForJavaType( Character.class );
}
return characterType;
}
@Override
public ServiceRegistry getServiceRegistry() {
return serviceRegistry;

View File

@ -58,7 +58,7 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
matchExpression.applyInferableType( expressibleType );
pattern.applyInferableType( expressibleType );
if ( escapeCharacter != null ) {
escapeCharacter.applyInferableType( expressibleType );
escapeCharacter.applyInferableType( nodeBuilder.getCharacterType() );
}
}

View File

@ -104,7 +104,7 @@ public class ILikeTest {
scope.inTransaction(
session -> {
Query q = session.createQuery( "from BasicEntity be where be.data like 'Pr%$_%' escape :esc" )
.setParameter("esc", "$");
.setParameter("esc", '$');
List l = q.getResultList();
assertEquals( 2, l.size() );
}