Skip to content

Commit

Permalink
MID-6239 fix: generated HQL LIKE uses escape character for _ and %
Browse files Browse the repository at this point in the history
  • Loading branch information
virgo47 committed Jan 4, 2021
1 parent f444426 commit c499f0f
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 13 deletions.
Expand Up @@ -965,7 +965,7 @@ public void test0112QueryUserSubstringFullName() throws Exception {
"from\n" +
" RUser u\n" +
"where\n" +
" lower(u.fullName.norm) like :norm";
" lower(u.fullName.norm) like :norm escape '!'";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
close(session);
Expand Down Expand Up @@ -1006,7 +1006,7 @@ public void test0116QuerySubstringMultivalued() throws Exception {
" RObject o\n" +
" left join o.employeeType e\n" +
"where\n" +
" e like :e\n";
" e like :e escape '!'\n";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
close(session);
Expand Down Expand Up @@ -2270,7 +2270,7 @@ public void test0530queryObjectSubstringName() throws Exception {
"from\n" +
" RObject o\n" +
"where\n" +
" o.name.orig like :orig\n";
" o.name.orig like :orig escape '!'\n";
assertEqualsIgnoreWhitespace(expected, real);

OperationResult result = new OperationResult("test0540queryObjectClassTypeUser");
Expand Down Expand Up @@ -3361,8 +3361,8 @@ public void test0750DereferenceLink() throws Exception {
" left join l.target t\n" +
"where\n" +
" (\n" +
" t.nameCopy.orig like :orig and\n" +
" t.nameCopy.norm like :norm\n" +
" t.nameCopy.orig like :orig escape '!' and\n" +
" t.nameCopy.norm like :norm escape '!'\n" +
" )\n";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
Expand Down Expand Up @@ -3394,7 +3394,7 @@ public void test0752DereferenceLinkedResourceName() throws Exception {
" left join l.target t\n" +
" left join t.resourceRef.target t2\n" +
"where\n" +
" t2.name.norm like :norm\n";
" t2.name.norm like :norm escape '!'\n";
assertEqualsIgnoreWhitespace(expected, real);

} finally {
Expand Down Expand Up @@ -3795,7 +3795,7 @@ public void test0940FullTextSimple() throws Exception {
+ " RUser u\n"
+ " left join u.textInfoItems t\n"
+ "where\n"
+ " t.text like :text";
+ " t.text like :text escape '!'";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
close(session);
Expand All @@ -3819,7 +3819,7 @@ public void test0941FullTextEmpty() throws Exception {
+ " RUser u\n"
+ " left join u.textInfoItems t\n"
+ "where\n"
+ " t.text like :text";
+ " t.text like :text escape '!'";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
close(session);
Expand All @@ -3845,8 +3845,8 @@ public void test0945FullTextMulti() throws Exception {
+ " left join u.textInfoItems t2\n"
+ "where\n"
+ " (\n"
+ " t.text like :text and\n"
+ " t2.text like :text2\n"
+ " t.text like :text escape '!' and\n"
+ " t2.text like :text2 escape '!'\n"
+ " )";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
Expand Down Expand Up @@ -4280,15 +4280,15 @@ public void test1230ApplicableDistinctAndOrderBy() throws Exception {
+ " select distinct\n"
+ " u.oid\n"
+ " from\n"
+ " RUser u left join u.employeeType e where e like :e)\n"
+ " RUser u left join u.employeeType e where e like :e escape '!')\n"
+ "order by u.nameCopy.orig asc";
} else {
expected = "select distinct\n"
+ " u.oid,\n"
+ " u.fullObject,\n"
+ " u.nameCopy.orig\n"
+ "from\n"
+ " RUser u left join u.employeeType e where e like :e\n"
+ " RUser u left join u.employeeType e where e like :e escape '!'\n"
+ "order by u.nameCopy.orig asc\n";
}
assertEqualsIgnoreWhitespace(expected, real);
Expand Down Expand Up @@ -4432,7 +4432,7 @@ public void test1280ApplicableDistinctWithCount() throws Exception {
.item(UserType.F_EMPLOYEE_TYPE).startsWith("a")
.build();
String real = getInterpretedQuery2(session, UserType.class, query, true, distinct());
String expected = "select count(distinct u.oid) from RUser u left join u.employeeType e where e like :e";
String expected = "select count(distinct u.oid) from RUser u left join u.employeeType e where e like :e escape '!'";
assertEqualsIgnoreWhitespace(expected, real);
} finally {
close(session);
Expand Down
Expand Up @@ -11,6 +11,7 @@
import com.evolveum.midpoint.repo.sql.query2.hqm.condition.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.query.Query;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
Expand Down Expand Up @@ -175,7 +176,13 @@ public Condition createSimpleComparisonCondition(String propertyPath, Object val
return new SimpleComparisonCondition(this, propertyPath, value, comparatorSymbol, ignoreCase);
}

public static final char LIKE_ESCAPE_CHAR = '!';
private static final String LIKE_ESCAPED_CHARS = "_%" + LIKE_ESCAPE_CHAR;

public Condition createLike(String propertyPath, String value, MatchMode matchMode, boolean ignoreCase) {
if (StringUtils.containsAny(value, LIKE_ESCAPED_CHARS)) {
value = escapeLikeValue(value);
}
switch (matchMode) {
case ANYWHERE: value = "%" + value + "%"; break;
case START: value = value + "%"; break;
Expand All @@ -185,6 +192,19 @@ public Condition createLike(String propertyPath, String value, MatchMode matchMo
return new SimpleComparisonCondition(this, propertyPath, value, "like", ignoreCase);
}

private String escapeLikeValue(String value) {
StringBuilder sb = new StringBuilder(value);
for (int i = 0; i < sb.length(); i++) {
if (LIKE_ESCAPED_CHARS.indexOf(sb.charAt(i)) == -1) {
continue;
}

sb.insert(i, LIKE_ESCAPE_CHAR);
i += 1;
}
return sb.toString();
}

public AndCondition createAnd(Condition... conditions) {
return new AndCondition(this, conditions);
}
Expand Down
Expand Up @@ -50,6 +50,12 @@ public void dumpToHql(StringBuilder sb, int indent) {
String parameterNamePrefix = createParameterName(propertyPath);
String parameterName = rootHibernateQuery.addParameter(parameterNamePrefix, finalPropertyValue);
sb.append(finalPropertyPath).append(" ").append(operator).append(" :").append(parameterName);
// See RootHibernateQuery.createLike for the other part of the solution.
// Design note: probably a bit cyclic dependency, but the knowledge about escaping still
// needs to be on both places anyway, so it's less messy than an additional parameter.
if (operator.equals("like")) {
sb.append(" escape '" + RootHibernateQuery.LIKE_ESCAPE_CHAR + '\'');
}
}

@Override
Expand Down

0 comments on commit c499f0f

Please sign in to comment.