diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleHint.java b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java index 4ab164f98..166e19e08 100644 --- a/src/main/java/net/sf/jsqlparser/expression/OracleHint.java +++ b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java @@ -24,7 +24,7 @@ public class OracleHint extends ASTNodeAccessImpl implements Expression { private static final Pattern SINGLE_LINE = Pattern.compile("--\\+ *([^ ].*[^ ])"); private static final Pattern MULTI_LINE = - Pattern.compile("/\\*\\+ *([^ ].*[^ ]) *\\*+/", Pattern.MULTILINE | Pattern.DOTALL); + Pattern.compile("\\A/\\*\\+ *([^ ].*[^ ]) *\\*+/", Pattern.MULTILINE | Pattern.DOTALL); private String value; private boolean singleLine = false; diff --git a/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java index 2da69721c..687c29ad5 100644 --- a/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java @@ -9,6 +9,10 @@ */ package net.sf.jsqlparser.expression; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; + +import java.time.Duration; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Test; @@ -46,4 +50,18 @@ void testMerge() throws JSQLParserException { TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); } + @Test + void testCraftedHintCommentDoesNotBacktrack() { + final StringBuilder sb = new StringBuilder("-- /*+ "); + for (int i = 0; i < 100000; i++) { + sb.append('*'); + } + final String crafted = sb.toString(); + + // a line comment carrying an unterminated /*+ marker (no closing */) used to make the + // block hint pattern backtrack quadratically, and it is not an oracle hint anyway + assertTimeoutPreemptively(Duration.ofSeconds(2), + () -> assertFalse(OracleHint.isHintMatch(crafted))); + } + }