diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 2328a266b9..0d596fe1d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -278,7 +278,8 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" ); private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" ); - private static final Pattern QUERY_PATTERN = Pattern.compile( "^\\s*(select\\b.+?\\bfrom\\b.+?)(\\b(where|join)\\b.+?)$" ); + private static final Pattern QUERY_PATTERN = Pattern.compile( + "^\\s*(select\\b.+?\\bfrom\\b.+?)(\\b(?:natural )?(?:left |right )?(?:inner |outer )?join.+?\\b)?(\\bwhere\\b.+?)$"); private static final CoreMessageLogger LOG = Logger.getMessageLogger( MethodHandles.lookup(), CoreMessageLogger.class, Dialect.class.getName() ); @@ -4767,13 +4768,16 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun public static String addQueryHints(String query, String hints) { Matcher matcher = QUERY_PATTERN.matcher( query ); if ( matcher.matches() && matcher.groupCount() > 1 ) { - String startToken = matcher.group( 1 ); - String endToken = matcher.group( 2 ); + final String startToken = matcher.group(1); + // Null if there is no join in the query + final String joinToken = Objects.toString(matcher.group(2), ""); + final String endToken = matcher.group(3); return startToken + " use index (" + hints + ") " + + joinToken + endToken; } else { diff --git a/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java b/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java new file mode 100644 index 0000000000..c24f4dfe73 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Check if IndexQueryHintHandler handles correctly simple query and query with JOIN + * + * @author Rguihard + */ +class DialectTest { + + static Stream _addQueryHints() { + final Stream.Builder builder = Stream.builder(); + + final String hints = "MY_INDEX"; + final String simpleQuery = "select COUNT(*) from TEST t1_0 where column1 = 'value'"; + builder.add( + Arguments.of("Simple query : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) where column1 = 'value'", simpleQuery, hints)); + final String joinQueryUsing = "select COUNT(*) from TEST t1_0 join TEST2 t2_0 using(column2) where field = 'value'"; + builder.add(Arguments.of("Join query with using : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 using(column2) where field = 'value'", + joinQueryUsing, hints)); + final String joinQueryOn = "select COUNT(*) from TEST t1_0 join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'"; + builder.add(Arguments.of("Join query with on : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'", + joinQueryOn, hints)); + final String leftJoinQuery = "select COUNT(*) from TEST t1_0 left join TEST2 t2_0 on t1_0.column2 = t2_0.column2 and t1_0.column3 = t2_0.column3 where field = 'value'"; + builder.add(Arguments.of("Left join query with on : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) left join TEST2 t2_0 on t1_0.column2 = t2_0.column2 and t1_0.column3 = t2_0.column3 where field = 'value'", + leftJoinQuery, hints)); + + return builder.build(); + } + + @MethodSource("_addQueryHints") + @ParameterizedTest + void addQueryHints(String description, String expected, String query, String hints) { + final String queryWithHint = MySQLDialect.addQueryHints(query, hints); + assertEquals(expected, queryWithHint, description); + } + +}