FIX: correct behavior of "startsWith, "contains" etc (#1031)

* FIX: correct behavior of "startsWith" if string contains "like"-wildcards

* Removed likeClasues with disabled escaping in database platforms as bind values are properly escaped now.
This commit is contained in:
Roland Praml
2017-06-19 22:21:26 +12:00
committed by Rob Bygrave
parent a1f95bf25e
commit bc74d35651
13 changed files with 162 additions and 15 deletions
+4 -4
View File
@@ -6,22 +6,22 @@ package io.ebean;
public enum LikeType {
/**
* You need to put in your own wildcards.
* You need to put in your own wildcards - no escaping is performed.
*/
RAW,
/**
* The % wildcard is added to the end of the search word.
* The % wildcard is added to the end of the search word and search word is escaped.
*/
STARTS_WITH,
/**
* The % wildcard is added to the beginning of the search word.
* The % wildcard is added to the beginning of the search word and search word is escaped.
*/
ENDS_WITH,
/**
* The % wildcard is added to the beginning and end of the search word.
* The % wildcard is added to the beginning and end of the search word and search word is escaped.
*/
CONTAINS,
@@ -174,6 +174,8 @@ public class DatabasePlatform {
protected boolean supportsNativeIlike;
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
protected char[] specialLikeCharacters = { '%', '_' };
/**
* Instantiates a new database platform.
@@ -617,4 +619,37 @@ public class DatabasePlatform {
logger.error("Error closing resultSet", e);
}
}
/**
* Escapes the like string for this DB-Platform
*/
public String escapeLikeString(String value) {
StringBuilder sb = null;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
boolean escaped = false;
for (char escapeChar: specialLikeCharacters) {
if (ch == escapeChar) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
}
escapeLikeCharacter(escapeChar, sb);
escaped = true;
break;
}
}
if (!escaped && sb != null) {
sb.append(ch);
}
}
if (sb == null) {
return value;
} else {
return sb.toString();
}
}
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('\\').append(ch);
}
}
@@ -40,10 +40,6 @@ public class H2Platform extends DatabasePlatform {
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
// like ? escape'' not working in the latest version H2 so just using no
// escape clause for now noting that backslash is an escape char for like in H2
this.likeClause = "like ?";
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
}
@@ -30,7 +30,6 @@ public class MySqlPlatform extends DatabasePlatform {
super();
this.platform = Platform.MYSQL;
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.platformDdl = new MySqlDdl(this);
@@ -28,7 +28,6 @@ public class PostgresPlatform extends DatabasePlatform {
super();
this.platform = Platform.POSTGRES;
this.supportsNativeIlike = true;
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
@@ -41,6 +41,8 @@ public class SqlServerPlatform extends DatabasePlatform {
this.openQuote = "[";
this.closeQuote = "]";
this.specialLikeCharacters = new char[] { '%', '_', '[' };
this.likeClause = "like ? COLLATE Latin1_General_BIN";
booleanDbType = Types.INTEGER;
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
@@ -63,4 +65,9 @@ public class SqlServerPlatform extends DatabasePlatform {
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('[').append(ch).append(']');
}
}
@@ -65,4 +65,9 @@ public interface SpiExpressionRequest {
* Append a DB Like clause.
*/
void appendLike();
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
}
@@ -96,6 +96,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public String getDBLikeClause() {
return ebeanServer.getDatabasePlatform().getLikeClause();
}
/**
* Return the database platform escaped like string.
*/
@Override
public String escapeLikeString(String value) {
return ebeanServer.getDatabasePlatform().escapeLikeString(value);
}
@Override
public void executeSecondaryQueries(boolean forEach) {
@@ -123,6 +123,11 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
* Return the Database platform like clause.
*/
String getDBLikeClause();
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
/**
* Mark the underlying transaction as not being query only.
@@ -93,6 +93,11 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
sql.append(" ");
}
@Override
public String escapeLikeString(String value) {
return queryRequest.escapeLikeString(value);
}
/**
* Increments the parameter index and returns that value.
*/
@@ -34,8 +34,7 @@ class LikeExpression extends AbstractValueExpression {
String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue();
request.addBindEncryptKey(encryptKey);
}
String bindValue = getValue(strValue(), caseInsensitive, type);
String bindValue = getValue(strValue(), caseInsensitive, type, request);
request.addBindValue(bindValue);
}
@@ -92,13 +91,15 @@ class LikeExpression extends AbstractValueExpression {
return strValue().equals(that.strValue());
}
private static String getValue(String value, boolean caseInsensitive, LikeType type) {
private static String getValue(String value, boolean caseInsensitive, LikeType type, SpiExpressionRequest request) {
if (caseInsensitive) {
value = value.toLowerCase();
}
if (type == LikeType.RAW) {
return value;
}
value = request.escapeLikeString(value);
switch (type) {
case RAW:
return value;
case STARTS_WITH:
return value + "%";
case ENDS_WITH: