DATACASS-121 - Automatically quote string-like parameter values in @Query methods

This commit is contained in:
Matthew Adams
2014-04-03 17:08:13 -05:00
parent 200effb9a2
commit 9109bada64
8 changed files with 99 additions and 22 deletions

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.cassandra.core.cql;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.datastax.driver.core.DataType;
public class CqlStringUtils {
@@ -26,6 +30,8 @@ public class CqlStringUtils {
protected static final String EMPTY_STRING = "";
protected static final String TYPE_PARAMETER_PREFIX = "<";
protected static final String TYPE_PARAMETER_SUFFIX = ">";
public static final String ISO8601_DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
public static final DateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat(ISO8601_DATE_FORMAT_PATTERN);
public static StringBuilder noNull(StringBuilder sb) {
return sb == null ? new StringBuilder() : sb;
@@ -128,4 +134,12 @@ public class CqlStringUtils {
}
return s.substring(1, s.length() - 1);
}
public static String date(Date date) {
return ISO8601_DATE_FORMAT.format(date);
}
public static String date(long time) {
return ISO8601_DATE_FORMAT.format(new Date(time));
}
}

View File

@@ -35,6 +35,12 @@ public class CassandraQueryMethod extends QueryMethod {
Long.class, boolean.class, Boolean.class, BigDecimal.class, BigInteger.class, double.class, Double.class,
float.class, Float.class, InetAddress.class, Date.class, UUID.class, }));
public static final List<Class<?>> STRING_LIKE_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { CharSequence.class, char.class, Character.class, char[].class }));
public static final List<Class<?>> DATE_PARAMETER_TYPES = Collections.unmodifiableList(Arrays
.asList(new Class<?>[] { Date.class }));
public static boolean isMapOfCharSequenceToObject(TypeInformation<?> type) {
if (!type.isMap()) {
@@ -52,6 +58,8 @@ public class CassandraQueryMethod extends QueryMethod {
protected Query query;
protected String queryString;
protected boolean queryCached = false;
protected Set<Integer> stringLikeParameterIndexes = new HashSet<Integer>();
protected Set<Integer> dateParameterIndexes = new HashSet<Integer>();
public CassandraQueryMethod(Method method, RepositoryMetadata metadata, CassandraMappingContext mappingContext) {
@@ -73,10 +81,23 @@ public class CassandraQueryMethod extends QueryMethod {
}
Set<Class<?>> offendingTypes = new HashSet<Class<?>>();
int i = 0;
for (Class<?> type : method.getParameterTypes()) {
if (!ALLOWED_PARAMETER_TYPES.contains(type)) {
offendingTypes.add(type);
}
for (Class<?> quotedType : STRING_LIKE_PARAMETER_TYPES) {
if (quotedType.isAssignableFrom(type)) {
stringLikeParameterIndexes.add(i);
}
}
for (Class<?> quotedType : DATE_PARAMETER_TYPES) {
if (quotedType.isAssignableFrom(type)) {
dateParameterIndexes.add(i);
}
}
i++;
}
if (offendingTypes.size() > 0) {
@@ -153,4 +174,12 @@ public class CassandraQueryMethod extends QueryMethod {
return isMapOfCharSequenceToObject(type.getComponentType());
}
public boolean isStringLikeParameter(int parameterIndex) {
return stringLikeParameterIndexes.contains(parameterIndex);
}
public boolean isDateParameter(int parameterIndex) {
return dateParameterIndexes.contains(parameterIndex);
}
}

View File

@@ -1,10 +1,12 @@
package org.springframework.data.cassandra.repository.query;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.cql.CqlStringUtils;
import org.springframework.data.cassandra.core.CassandraOperations;
public class StringBasedCassandraQuery extends AbstractCassandraQuery {
@@ -38,7 +40,19 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
while (matcher.find()) {
String group = matcher.group();
int index = Integer.parseInt(matcher.group(1));
result = result.replace(group, getParameterWithIndex(accessor, index).toString());
Object value = getParameterWithIndex(accessor, index);
String stringValue = null;
CassandraQueryMethod queryMethod = getQueryMethod();
if (queryMethod.isStringLikeParameter(index)) {
stringValue = "'" + CqlStringUtils.escapeSingle(value) + "'";
} else if (queryMethod.isDateParameter(index)) {
stringValue = "'" + CqlStringUtils.date((Date) value) + "'";
} else {
stringValue = value.toString();
}
result = result.replace(group, stringValue);
}
return result;

View File

@@ -248,4 +248,24 @@ public abstract class QueryIntegrationTests extends AbstractSpringDataEmbeddedCa
assertEquals(found.getFirstname(), saved.getFirstname());
}
@Test
public void testEscapeSingleQuoteInQueryParameterValue() {
Person saved = new Person();
saved.setFirstname("Bri'an" + uuid());
String lastname = "O'Brian" + uuid();
saved.setLastname(lastname);
saved = r.save(saved);
List<Person> results = r.findFolksWithLastnameAsList(lastname);
assertNotNull(results);
assertTrue(results.size() == 1);
for (Person person : results) {
assertNotNull(person);
assertEquals(saved.getLastname(), person.getLastname());
assertEquals(saved.getFirstname(), person.getFirstname());
}
}
}

View File

@@ -100,10 +100,10 @@ public class StringBasedCassandraQueryIntegrationTests {
private interface SampleRepository {
@Query("SELECT * FROM person WHERE lastname='?0';")
@Query("SELECT * FROM person WHERE lastname=?0;")
Person findByLastname(String lastname);
@Query("SELECT * FROM person WHERE lastname='?0' AND firstname='?1';")
@Query("SELECT * FROM person WHERE lastname=?0 AND firstname=?1;")
Person findByLastnameAndFirstname(String lastname, String firstname);
}
}

View File

@@ -17,38 +17,38 @@ import com.datastax.driver.core.ResultSet;
public interface PersonRepositoryWithQueryAnnotations extends PersonRepository {
@Override
@Query("select * from person where lastname = '?0'")
@Query("select * from person where lastname = ?0")
List<Person> findFolksWithLastnameAsList(String lastname);
@Override
@Query("select * from person where lastname = '?0'")
@Query("select * from person where lastname = ?0")
ResultSet findFolksWithLastnameAsResultSet(String last);
@Override
@Query("select * from person where lastname = '?0'")
@Query("select * from person where lastname = ?0")
Person[] findFolksWithLastnameAsArray(String lastname);
@Override
@Query("select * from person where lastname = '?0' and firstname = '?1'")
@Query("select * from person where lastname = ?0 and firstname = ?1")
Person findSingle(String last, String first);
@Override
@Query("select * from person where lastname = '?0'")
@Query("select * from person where lastname = ?0")
List<Map<String, Object>> findFolksWithLastnameAsListOfMapOfStringToObject(String last);
@Override
@Query("select nickname from person where lastname = '?0' and firstname = '?1'")
@Query("select nickname from person where lastname = ?0 and firstname = ?1")
String findSingleNickname(String last, String first);
@Override
@Query("select birthdate from person where lastname = '?0' and firstname = '?1'")
@Query("select birthdate from person where lastname = ?0 and firstname = ?1")
Date findSingleBirthdate(String last, String first);
@Override
@Query("select cool from person where lastname = '?0' and firstname = '?1'")
@Query("select cool from person where lastname = ?0 and firstname = ?1")
boolean findSingleCool(String last, String first);
@Override
@Query("select numberofchildren from person where lastname = '?0' and firstname = '?1'")
@Query("select numberofchildren from person where lastname = ?0 and firstname = ?1")
int findSingleNumberOfChildren(String last, String first);
}

View File

@@ -1,9 +1,9 @@
Person.findFolksWithLastnameAsList=select * from person where lastname = '?0'
Person.findFolksWithLastnameAsResultSet=select * from person where lastname = '?0'
Person.findFolksWithLastnameAsArray=select * from person where lastname = '?0'
Person.findSingle=select * from person where lastname = '?0' and firstname = '?1'
Person.findFolksWithLastnameAsListOfMapOfStringToObject=select * from person where lastname = '?0'
Person.findSingleNickname=select nickname from person where lastname = '?0' and firstname = '?1'
Person.findSingleBirthdate=select birthdate from person where lastname = '?0' and firstname = '?1'
Person.findSingleCool=select cool from person where lastname = '?0' and firstname = '?1'
Person.findSingleNumberOfChildren=select numberofchildren from person where lastname = '?0' and firstname = '?1'
Person.findFolksWithLastnameAsList=select * from person where lastname = ?0
Person.findFolksWithLastnameAsResultSet=select * from person where lastname = ?0
Person.findFolksWithLastnameAsArray=select * from person where lastname = ?0
Person.findSingle=select * from person where lastname = ?0 and firstname = ?1
Person.findFolksWithLastnameAsListOfMapOfStringToObject=select * from person where lastname = ?0
Person.findSingleNickname=select nickname from person where lastname = ?0 and firstname = ?1
Person.findSingleBirthdate=select birthdate from person where lastname = ?0 and firstname = ?1
Person.findSingleCool=select cool from person where lastname = ?0 and firstname = ?1
Person.findSingleNumberOfChildren=select numberofchildren from person where lastname = ?0 and firstname = ?1

View File

@@ -1 +1 @@
User.findByNamedQuery=SELECT firstname FROM users WHERE username='?0'
User.findByNamedQuery=SELECT firstname FROM users WHERE username=?0