Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Nabil Fawwaz Elqayyim
* @since 4.0
*/
public final class PreprocessedQuery implements DeclaredQuery {
Expand Down Expand Up @@ -247,6 +248,7 @@ PreprocessedQuery parse(String query, Function<String, DeclaredQuery> declaredQu

String resultingQuery = parsedQuery.getQueryString();
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(resultingQuery);
java.util.BitSet commentPositions = findCommentPositions(resultingQuery);

ParameterBindings parameterBindings = new ParameterBindings(bindings, it -> checkAndRegister(it, bindings));
int currentIndex = 0;
Expand All @@ -255,7 +257,7 @@ PreprocessedQuery parse(String query, Function<String, DeclaredQuery> declaredQu

while (matcher.find()) {

if (parsedQuery.isQuoted(matcher.start())) {
if (parsedQuery.isQuoted(matcher.start()) || commentPositions.get(matcher.start())) {
continue;
}

Expand Down Expand Up @@ -625,4 +627,68 @@ public boolean hasLabels() {
}
}

private static java.util.BitSet findCommentPositions(String query) {
int queryLen = query.length();
java.util.BitSet isComment = new java.util.BitSet(queryLen);
boolean inBlockComment = false;
boolean inLineComment = false;
boolean inString = false;

for (int i = 0; i < queryLen; i++) {
char c = query.charAt(i);
char next = (i + 1 < queryLen) ? query.charAt(i + 1) : '\0';

// String Literal State: Comment markers are treated as normal characters
if (inString) {
if (c == '\'') {
// Handle SQL-style escaped quotes: ''
if (next == '\'') {
i++;
} else {
inString = false;
}
}
continue;
}

// Block Comment State: Identify positions within /* ... */
if (inBlockComment) {
isComment.set(i);
if (c == '*' && next == '/') {
isComment.set(i + 1);
inBlockComment = false;
i++;
}
continue;
}

// Line Comment State: Identify positions within -- or //
if (inLineComment) {
isComment.set(i);
// End line comment state on newline or carriage return
if (c == '\n' || c == '\r') {
inLineComment = false;
}
continue;
}

// Entering States: Check for quotes or comment markers
if (c == '\'') {
inString = true;
} else if (c == '/' && next == '*') {
// Start of block comment
inBlockComment = true;
isComment.set(i);
isComment.set(i + 1);
i++;
} else if ((c == '-' && next == '-') || (c == '/' && next == '/')) {
// Start of line comment (both SQL-style '--' and Java-style '//')
inLineComment = true;
isComment.set(i);
isComment.set(i + 1);
i++;
}
}
return isComment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* @author Jens Schauder
* @author Diego Krupitza
* @author Mark Paluch
* @author Nabil Fawwaz Elqayyim
*/
class PreprocessedQueryUnitTests {

Expand All @@ -52,4 +53,40 @@ static Stream<Arguments.ArgumentSet> parameters() {
Arguments.argumentSet("no parameter", "select u from User u where u.email = u.email", false));
}

@ParameterizedTest // GH-4090
@MethodSource("commentParameters")
void shouldIgnoreMarkersInComments(String queryText, int expectedBindingCount, String description) {

// Use the official static factory method from the DeclaredQuery interface
DeclaredQuery declaredQuery = DeclaredQuery.jpqlQuery(queryText);
PreprocessedQuery preprocessed = PreprocessedQuery.parse(declaredQuery);

assertThat(preprocessed.getBindings())
.as(description)
.hasSize(expectedBindingCount);
}

static Stream<Arguments> commentParameters() {
return Stream.of(
// 1. Block Comment Scenarios
Arguments.of("SELECT e FROM Entity e /* block ? */ WHERE e.id = :id", 1, "Standard block comment"),
Arguments.of("SELECT e FROM Entity e /* asterisk * inside */ WHERE e.id = :id", 1, "Asterisk inside block comment"),

// 2. SQL-style Line Comment Scenarios
Arguments.of("SELECT e FROM Entity e -- line comment \n WHERE e.id = :id", 1, "SQL-style line comment with newline"),

// 3. Java-style Line Comment Scenarios
// This specific case turns the remaining yellow branches green.
Arguments.of("SELECT e FROM Entity e // java comment \r WHERE e.id = :id", 1, "Java-style line comment with carriage return"),

// 4. Mathematical Operators
// Ensures '/' and '-' are not always treated as comment starts.
Arguments.of("SELECT e FROM Entity e WHERE e.id / 2 = 1 AND e.id * 2 = :id", 1, "Slash and asterisk as operators"),
Arguments.of("SELECT e FROM Entity e WHERE e.id - 1 = :id", 1, "Hyphen as subtraction operator"),

// 5. String Literal and Escaping Scenarios
// Confirms that markers inside quotes are ignored and escaped quotes are handled.
Arguments.of("SELECT e FROM Entity e WHERE e.name = 'It''s a -- marker' AND e.id = :id", 1, "Markers inside string literals")
);
}
}