Skip to content

Commit 4aabc05

Browse files
paleszAndras Palinkas
authored and
Andras Palinkas
committed
SQL: Escaped wildcard (*) not accepted in LIKE (#63428)
For a query like `SELECT name FROM test WHERE name LIKE ''%c*'` ES SQL generates an error. `*` is not a special character in a `LIKE` construct and it's expected to not needing to be escaped, so the previous query should work as is. In the LIKE pattern any `*` character was treated as invalid character and the usage of `%` or `_` was suggested instead. But `*` is a valid, acceptable non-wildcard on the right side of the `LIKE` operator. Fix: #55108 (cherry picked from commit 190d9fe)
1 parent f70391c commit 4aabc05

File tree

4 files changed

+47
-22
lines changed

4 files changed

+47
-22
lines changed

docs/reference/sql/functions/like-rlike.asciidoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ with the `LIKE` operator:
3333
* The percent sign (%)
3434
* The underscore (_)
3535

36-
The percent sign represents zero, one or multiple characters. The underscore represents a single number or character. These symbols can be
37-
used in combinations.
36+
The percent sign represents zero, one or multiple characters. The underscore represents a single number or character. These symbols can be used in combinations.
37+
38+
NOTE: No other characters have special meaning or act as wildcard. Characters often used as wildcards in other languages (`*` or `?`) are treated as normal characters.
3839

3940
[source, sql]
4041
----

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,6 @@ public LikePattern visitPattern(PatternContext ctx) {
270270
if (pattern == null) {
271271
throw new ParsingException(source(ctx.value), "Pattern must not be [null]");
272272
}
273-
int pos = pattern.indexOf('*');
274-
if (pos >= 0) {
275-
throw new ParsingException(source(ctx.value),
276-
"Invalid char [*] found in pattern [{}] at position {}; use [%] or [_] instead",
277-
pattern, pos);
278-
}
279273

280274
char escape = 0;
281275
PatternEscapeContext escapeCtx = ctx.patternEscape();
@@ -288,7 +282,7 @@ public LikePattern visitPattern(PatternContext ctx) {
288282
} else if (escapeString.length() == 1) {
289283
escape = escapeString.charAt(0);
290284
// these chars already have a meaning
291-
if (escape == '*' || escape == '%' || escape == '_') {
285+
if (escape == '%' || escape == '_') {
292286
throw new ParsingException(source(escapeCtx.escape), "Char [{}] cannot be used for escaping", escape);
293287
}
294288
// lastly validate that escape chars (if present) are followed by special chars
@@ -303,8 +297,8 @@ public LikePattern visitPattern(PatternContext ctx) {
303297
char next = pattern.charAt(i + 1);
304298
if (next != '%' && next != '_') {
305299
throw new ParsingException(source(ctx.value),
306-
"Pattern [{}] is invalid as escape char [{}] at position {} can only escape wildcard chars; found [{}]",
307-
pattern, escape, i, next);
300+
"Pattern [{}] is invalid as escape char [{}] at position {} can only escape "
301+
+ "wildcard chars [%_]; found [{}]", pattern, escape, i, next);
308302
}
309303
}
310304
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public class LikeEscapingParsingTests extends ESTestCase {
2121

2222
private final SqlParser parser = new SqlParser();
2323

24+
private static LikePattern patternOfLike(Expression exp) {
25+
assertThat(exp, instanceOf(Like.class));
26+
Like l = (Like) exp;
27+
return l.pattern();
28+
}
29+
2430
private String error(String pattern) {
2531
ParsingException ex = expectThrows(ParsingException.class,
2632
() -> parser.createExpression(format(null, "exp LIKE {}", pattern)));
@@ -36,9 +42,11 @@ private LikePattern like(String pattern) {
3642
} else {
3743
exp = parser.createExpression(format(null, "exp LIKE '{}'", pattern));
3844
}
39-
assertThat(exp, instanceOf(Like.class));
40-
Like l = (Like) exp;
41-
return l.pattern();
45+
return patternOfLike(exp);
46+
}
47+
48+
private LikePattern like(String pattern, Character escapeChar) {
49+
return patternOfLike(parser.createExpression(format(null, "exp LIKE '{}' ESCAPE '{}'", pattern, escapeChar)));
4250
}
4351

4452
public void testNoEscaping() {
@@ -55,16 +63,34 @@ public void testEscapingLastChar() {
5563

5664
public void testEscapingWrongChar() {
5765
assertThat(error("'|string' ESCAPE '|'"),
58-
is("line 1:11: Pattern [|string] is invalid as escape char [|] at position 0 can only escape wildcard chars; found [s]"));
66+
is("line 1:11: Pattern [|string] is invalid as escape char [|] at position 0 can only escape "
67+
+ "wildcard chars [%_]; found [s]"));
68+
}
69+
70+
public void testEscapingTheEscapeCharacter() {
71+
assertThat(error("'||string' ESCAPE '|'"),
72+
is("line 1:11: Pattern [||string] is invalid as escape char [|] at position 0 can only escape wildcard chars [%_]; found [|]"));
5973
}
6074

61-
public void testInvalidChar() {
62-
assertThat(error("'%string' ESCAPE '%'"),
63-
is("line 1:28: Char [%] cannot be used for escaping"));
75+
public void testEscapingWildcards() {
76+
assertThat(error("'string' ESCAPE '%'"),
77+
is("line 1:27: Char [%] cannot be used for escaping"));
78+
assertThat(error("'string' ESCAPE '_'"),
79+
is("line 1:27: Char [_] cannot be used for escaping"));
6480
}
6581

66-
public void testCannotUseStar() {
67-
assertThat(error("'|*string' ESCAPE '|'"),
68-
is("line 1:11: Invalid char [*] found in pattern [|*string] at position 1; use [%] or [_] instead"));
82+
public void testCanUseStarWithoutEscaping() {
83+
LikePattern like = like("%string*");
84+
assertThat(like.pattern(), is("%string*"));
85+
assertThat(like.asJavaRegex(), is("^.*string\\*$"));
86+
assertThat(like.asLuceneWildcard(), is("*string\\*"));
6987
}
88+
89+
public void testEscapingWithStar() {
90+
LikePattern like = like("*%%*__string", '*');
91+
assertThat(like.pattern(), is("*%%*__string"));
92+
assertThat(like.asJavaRegex(), is("^%.*_.string$"));
93+
assertThat(like.asLuceneWildcard(), is("%*_?string"));
94+
}
95+
7096
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/util/LikeConversionTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ public void testWildcardEscapeLuceneWildcard() {
8181
assertEquals("foo\\*bar*", wildcard("foo*bar%"));
8282
}
8383

84+
public void testStarLiteralWithWildcards() {
85+
assertEquals("\\**\\*?foo\\*\\*?*", wildcard("*%*_foo**_%"));
86+
}
87+
8488
public void testWildcardEscapedWildcard() {
8589
assertEquals("foo\\*bar%", wildcard("foo*bar|%"));
8690
}
@@ -129,4 +133,4 @@ public void testUnescapeMultipleEscapes() {
129133
assertEquals("foo|_bar|", unescape("foo|||_bar||"));
130134
}
131135

132-
}
136+
}

0 commit comments

Comments
 (0)