Skip to content

Commit 86763dd

Browse files
renechoivelo
andauthored
fix: correct escape & anchor handling in likeToRegex/regexToLike (#1174)
* fix: correct escape & anchor handling in likeToRegex/regexToLike - treat \% \_ as literals, escape regex metachars, strip outer ^/$ only - add unit tests to guarantee accurate LIKE ↔ regex round-trips * Fix code format Signed-off-by: Marvin Froeder <[email protected]> --------- Signed-off-by: Marvin Froeder <[email protected]> Co-authored-by: Marvin Froeder <[email protected]>
1 parent 7876d3d commit 86763dd

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/types/ExpressionUtils.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,21 @@ public static Expression<String> likeToRegex(Expression<String> expr, boolean ma
529529
if (matchStartAndEnd && !like.startsWith("%")) {
530530
rv.append('^');
531531
}
532+
boolean escape = false;
532533
for (var i = 0; i < like.length(); i++) {
533534
var ch = like.charAt(i);
535+
if (escape) {
536+
if (ch == '.' || ch == '*' || ch == '?') {
537+
rv.append('\\');
538+
}
539+
rv.append(ch);
540+
escape = false;
541+
continue;
542+
}
543+
if (ch == '\\') {
544+
escape = true;
545+
continue;
546+
}
534547
if (ch == '.' || ch == '*' || ch == '?') {
535548
rv.append('\\');
536549
} else if (ch == '%') {
@@ -601,12 +614,23 @@ public static <T> Expression<T> list(Class<T> clazz, List<? extends Expression<?
601614
public static Expression<String> regexToLike(Expression<String> expr) {
602615
if (expr instanceof Constant<?>) {
603616
final var str = expr.toString();
604-
final var rv = new StringBuilder(str.length() + 2);
617+
// final var rv = new StringBuilder(str.length() + 2);
618+
int start = 0;
619+
int end = str.length();
620+
if (start < end && str.charAt(start) == '^') {
621+
start++;
622+
}
623+
if (end > start && str.charAt(end - 1) == '$') {
624+
end--;
625+
}
626+
final var rv = new StringBuilder(end - start + 2);
605627
var escape = false;
606-
for (var i = 0; i < str.length(); i++) {
628+
// for (var i = 0; i < str.length(); i++) {
629+
for (var i = start; i < end; i++) {
607630
final var ch = str.charAt(i);
608631
if (!escape && ch == '.') {
609-
if (i < str.length() - 1 && str.charAt(i + 1) == '*') {
632+
// if (i < str.length() - 1 && str.charAt(i + 1) == '*') {
633+
if (i < end - 1 && str.charAt(i + 1) == '*') {
610634
rv.append('%');
611635
i++;
612636
} else {
@@ -616,16 +640,23 @@ public static Expression<String> regexToLike(Expression<String> expr) {
616640
} else if (!escape && ch == '\\') {
617641
escape = true;
618642
continue;
619-
} else if (!escape && (ch == '[' || ch == ']' || ch == '^' || ch == '.' || ch == '*')) {
643+
// } else if (!escape && (ch == '[' || ch == ']' || ch == '^' || ch == '.' || ch == '*'))
644+
// {
645+
} else if (!escape
646+
&& (ch == '[' || ch == ']' || ch == '^' || ch == '$' || ch == '.' || ch == '*')) {
620647
throw new QueryException("'" + str + "' can't be converted to like form");
621648
} else if (escape
622649
&& (ch == 'd' || ch == 'D' || ch == 's' || ch == 'S' || ch == 'w' || ch == 'W')) {
623650
throw new QueryException("'" + str + "' can't be converted to like form");
624651
}
652+
if (ch == '%' || ch == '_') {
653+
rv.append('\\');
654+
}
625655
rv.append(ch);
626656
escape = false;
627657
}
628-
if (!rv.toString().equals(str)) {
658+
// if (!rv.toString().equals(str)) {
659+
if (start != 0 || end != str.length() || !rv.toString().equals(str.substring(start, end))) {
629660
return ConstantImpl.create(rv.toString());
630661
}
631662
} else if (expr instanceof Operation<?>) {

querydsl-libraries/querydsl-core/src/test/java/com/querydsl/core/types/ExpressionUtilsTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ public void likeToRegex_escape() {
7171
assertThat(regex(ConstantImpl.create("."))).isEqualTo("^\\.$");
7272
}
7373

74+
@Test
75+
public void likeToRegex_escapeCharacter() {
76+
assertThat(regex(ConstantImpl.create("a\\%b"))).isEqualTo("^a%b$");
77+
assertThat(regex(ConstantImpl.create("a\\_b"))).isEqualTo("^a_b$");
78+
}
79+
80+
@Test
81+
public void regexToLike_anchorsAndEscapes() {
82+
assertThat(like(ConstantImpl.create("^a%b$"))).isEqualTo("a\\%b");
83+
assertThat(like(ConstantImpl.create("^a_b$"))).isEqualTo("a\\_b");
84+
assertThat(like(ConstantImpl.create("^ab$"))).isEqualTo("ab");
85+
}
86+
7487
@Test
7588
public void regexToLike() {
7689
assertThat(like(ConstantImpl.create(".*"))).isEqualTo("%");

0 commit comments

Comments
 (0)