Skip to content

Commit 71986ac

Browse files
committed
Use ^ for like escaping to avoid dialect ambiguity
1 parent 474131b commit 71986ac

File tree

2 files changed

+67
-20
lines changed

2 files changed

+67
-20
lines changed

mug-guava/src/main/java/com/google/mu/safesql/SafeSql.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -556,16 +556,21 @@ && appendBeforeQuotedPlaceholder("'", placeholder, "'")) {
556556
} else if (lookbehind("LIKE '%", placeholder)
557557
&& appendBeforeQuotedPlaceholder("'%", placeholder, "%'")) {
558558
rejectEscapeAfter(placeholder);
559-
builder.addParameter(
560-
paramName, "%" + escapePercent(mustBeString(placeholder, value)) + "%");
559+
builder
560+
.addParameter(paramName, "%" + escapePercent(mustBeString(placeholder, value)) + "%")
561+
.appendSql(" ESCAPE '^'");
561562
} else if (lookbehind("LIKE '%", placeholder)
562563
&& appendBeforeQuotedPlaceholder("'%", placeholder, "'")) {
563564
rejectEscapeAfter(placeholder);
564-
builder.addParameter(paramName, "%" + escapePercent(mustBeString(placeholder, value)));
565+
builder
566+
.addParameter(paramName, "%" + escapePercent(mustBeString(placeholder, value)))
567+
.appendSql(" ESCAPE '^'");
565568
} else if (lookbehind("LIKE '", placeholder)
566569
&& appendBeforeQuotedPlaceholder("'", placeholder, "%'")) {
567570
rejectEscapeAfter(placeholder);
568-
builder.addParameter(paramName, escapePercent(mustBeString(placeholder, value)) + "%");
571+
builder
572+
.addParameter(paramName, escapePercent(mustBeString(placeholder, value)) + "%")
573+
.appendSql(" ESCAPE '^'");
569574
} else if (appendBeforeQuotedPlaceholder("'", placeholder, "'")) {
570575
builder.addParameter(paramName, mustBeString("'" + placeholder + "'", value));
571576
} else {
@@ -1072,7 +1077,7 @@ private static SafeSql subqueryOrParameter(CharSequence name, Object param) {
10721077
}
10731078

10741079
private static String escapePercent(String s) {
1075-
return first(c -> c == '\\' || c == '%' || c == '_').repeatedly().replaceAllFrom(s, c -> "\\" + c);
1080+
return first(c -> c == '^' || c == '%' || c == '_').repeatedly().replaceAllFrom(s, c -> "^" + c);
10761081
}
10771082

10781083
private static <T> Template<T> prepare(

mug-guava/src/test/java/com/google/mu/safesql/SafeSqlTest.java

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public void singleNullParameter() {
239239
@Test
240240
public void singleLikeParameterWithWildcardAtBothEnds() {
241241
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}%'", "foo");
242-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
242+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
243243
assertThat(sql.getParameters()).containsExactly("%foo%");
244244
}
245245

@@ -270,15 +270,29 @@ public void likeWithEscapeNotSuppoted_followedByPercentSign() {
270270
@Test
271271
public void literalPercentValueWithWildcardAtBothEnds() {
272272
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}%'", "%");
273-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
274-
assertThat(sql.getParameters()).containsExactly("%\\%%");
273+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
274+
assertThat(sql.getParameters()).containsExactly("%^%%");
275275
}
276276

277277
@Test
278278
public void literalBackslashValueWithWildcardAtBothEnds() {
279279
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}%'", "\\");
280-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
281-
assertThat(sql.getParameters()).containsExactly("%\\\\%");
280+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
281+
assertThat(sql.getParameters()).containsExactly("%\\%");
282+
}
283+
284+
@Test
285+
public void literalCaretValueWithWildcardAtBothEnds() {
286+
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}%'", "^");
287+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
288+
assertThat(sql.getParameters()).containsExactly("%^^%");
289+
}
290+
291+
@Test
292+
public void literalSingleQuoteValueWithWildcardAtBothEnds() {
293+
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}%'", "'");
294+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
295+
assertThat(sql.getParameters()).containsExactly("%'%");
282296
}
283297

284298
@Test
@@ -293,22 +307,36 @@ public void stringRequiredWhenWildcardsAtBothEnds() {
293307
@Test
294308
public void singleLikeParameterWithWildcardAsPrefix() {
295309
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}'", "foo");
296-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
310+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
297311
assertThat(sql.getParameters()).containsExactly("%foo");
298312
}
299313

300314
@Test
301315
public void literalPercentValueWithWildcardAtPrefix() {
302316
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}'", "%");
303-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
304-
assertThat(sql.getParameters()).containsExactly("%\\%");
317+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
318+
assertThat(sql.getParameters()).containsExactly("%^%");
305319
}
306320

307321
@Test
308322
public void literalBackslashValueWithWildcardAtPrefix() {
309323
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}'", "\\");
310-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
311-
assertThat(sql.getParameters()).containsExactly("%\\\\");
324+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
325+
assertThat(sql.getParameters()).containsExactly("%\\");
326+
}
327+
328+
@Test
329+
public void literalCaretValueWithWildcardAtPrefix() {
330+
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}'", "^");
331+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
332+
assertThat(sql.getParameters()).containsExactly("%^^");
333+
}
334+
335+
@Test
336+
public void literalSingleQuoteValueWithWildcardAtPrefix() {
337+
SafeSql sql = SafeSql.of("select * from tbl where name like '%{s}'", "'");
338+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
339+
assertThat(sql.getParameters()).containsExactly("%'");
312340
}
313341

314342
@Test
@@ -323,22 +351,36 @@ public void stringRequiredWhenWildcardsAsPrefix() {
323351
@Test
324352
public void singleLikeParameterWithWildcardAsSuffix() {
325353
SafeSql sql = SafeSql.of("select * from tbl where name like '{s}%'", "foo");
326-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
354+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
327355
assertThat(sql.getParameters()).containsExactly("foo%");
328356
}
329357

330358
@Test
331359
public void literalPercentValueWithWildcardAtSuffix() {
332360
SafeSql sql = SafeSql.of("select * from tbl where name like '{s}%'", "%");
333-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
334-
assertThat(sql.getParameters()).containsExactly("\\%%");
361+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
362+
assertThat(sql.getParameters()).containsExactly("^%%");
335363
}
336364

337365
@Test
338366
public void literalBackslashValueWithWildcardAtSuffix() {
339367
SafeSql sql = SafeSql.of("select * from tbl where name like '{s}%'", "\\");
340-
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ?");
341-
assertThat(sql.getParameters()).containsExactly("\\\\%");
368+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
369+
assertThat(sql.getParameters()).containsExactly("\\%");
370+
}
371+
372+
@Test
373+
public void literalCaretValueWithWildcardAtSuffix() {
374+
SafeSql sql = SafeSql.of("select * from tbl where name like '{s}%'", "^");
375+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
376+
assertThat(sql.getParameters()).containsExactly("^^%");
377+
}
378+
379+
@Test
380+
public void literalSingleQuoteValueWithWildcardAtSuffix() {
381+
SafeSql sql = SafeSql.of("select * from tbl where name like '{s}%'", "'");
382+
assertThat(sql.toString()).isEqualTo("select * from tbl where name like ? ESCAPE '^'");
383+
assertThat(sql.getParameters()).containsExactly("'%");
342384
}
343385

344386
@Test

0 commit comments

Comments
 (0)