Author: jbellis
Date: Sat May 7 23:16:15 2011
New Revision: 1100657
URL: http://svn.apache.org/viewvc?rev=1100657&view=rev
Log:
fix CQL treatment of> and <operators in range slices
patch by Pavel Yaskevich and jbellis for CASSANDRA-2592
Modified:
cassandra/branches/cassandra-0.8/CHANGES.txt
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
cassandra/branches/cassandra-0.8/test/system/test_cql.py
Modified: cassandra/branches/cassandra-0.8/CHANGES.txt
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/CHANGES.txt?rev=1100657&r1=1100656&r2=1100657&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8/CHANGES.txt Sat May 7 23:16:15 2011
@@ -5,6 +5,8 @@
* fix merkle tree splitting exiting early (CASSANDRA-2605)
* snapshot_before_compaction directory name fix (CASSANDRA-2598)
* Disable compaction throttling during bootstrap (CASSANDRA-2612)
+ * fix CQL treatment of > and < operators in range slices (CASSANDRA-2592)
+
0.8.0-beta2
* fix NPE compacting index CFs (CASSANDRA-2528)
Modified:
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java?rev=1100657&r1=1100656&r2=1100657&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
(original)
+++
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
Sat May 7 23:16:15 2011
@@ -76,11 +76,11 @@ public class QueryProcessor
private static List<org.apache.cassandra.db.Row> getSlice(String keyspace,
SelectStatement select)
throws InvalidRequestException, TimedOutException, UnavailableException
{
- List<org.apache.cassandra.db.Row> rows = null;
+ List<org.apache.cassandra.db.Row> rows;
QueryPath queryPath = new QueryPath(select.getColumnFamily());
AbstractType<?> comparator = select.getComparator(keyspace);
List<ReadCommand> commands = new ArrayList<ReadCommand>();
-
+
assert select.getKeys().size() == 1;
CFMetaData metadata = validateColumnFamily(keyspace,
select.getColumnFamily(), false);
@@ -125,20 +125,27 @@ public class QueryProcessor
{
throw new RuntimeException(e);
}
-
+
return rows;
}
private static List<org.apache.cassandra.db.Row> multiRangeSlice(String
keyspace, SelectStatement select)
throws TimedOutException, UnavailableException, InvalidRequestException
{
- List<org.apache.cassandra.db.Row> rows = null;
-
+ List<org.apache.cassandra.db.Row> rows;
+ IPartitioner<?> p = StorageService.getPartitioner();
+
AbstractType<?> keyType = DatabaseDescriptor.getCFMetaData(keyspace,
select.getColumnFamily()).getKeyValidator();
- ByteBuffer startKey = (select.getKeyStart() != null) ?
select.getKeyStart().getByteBuffer(keyType) : (new Term()).getByteBuffer();
- ByteBuffer finishKey = (select.getKeyFinish() != null) ?
select.getKeyFinish().getByteBuffer(keyType) : (new Term()).getByteBuffer();
- IPartitioner<?> p = StorageService.getPartitioner();
+
+ ByteBuffer startKey = (select.getKeyStart() != null)
+ ? select.getKeyStart().getByteBuffer(keyType)
+ : (new Term()).getByteBuffer();
+
+ ByteBuffer finishKey = (select.getKeyFinish() != null)
+ ? select.getKeyFinish().getByteBuffer(keyType)
+ : (new Term()).getByteBuffer();
+
AbstractBounds bounds = new Bounds(p.getToken(startKey),
p.getToken(finishKey));
CFMetaData metadata = validateColumnFamily(keyspace,
select.getColumnFamily(), false);
@@ -147,6 +154,10 @@ public class QueryProcessor
SlicePredicate thriftSlicePredicate = slicePredicateFromSelect(select,
comparator);
validateSlicePredicate(metadata, thriftSlicePredicate);
+ int limit = select.isKeyRange() && select.getKeyStart() != null
+ ? select.getNumRecords() + 1
+ : select.getNumRecords();
+
try
{
rows = StorageProxy.getRangeSlice(new RangeSliceCommand(keyspace,
@@ -154,8 +165,8 @@ public class QueryProcessor
null,
thriftSlicePredicate,
bounds,
-
select.getNumRecords()),
- select.getConsistencyLevel());
+ limit),
+
select.getConsistencyLevel());
}
catch (IOException e)
{
@@ -169,8 +180,23 @@ public class QueryProcessor
{
throw new TimedOutException();
}
-
- return rows;
+
+ // if start key was set and relation was "greater than"
+ if (select.getKeyStart() != null && !select.includeStartKey())
+ {
+ if (rows.get(0).key.key.equals(startKey))
+ rows.remove(0);
+ }
+
+ // if finish key was set and relation was "less than"
+ if (select.getKeyFinish() != null && !select.includeFinishKey())
+ {
+ int lastIndex = rows.size() - 1;
+ if (rows.get(lastIndex).key.key.equals(finishKey))
+ rows.remove(lastIndex);
+ }
+
+ return rows.subList(0, select.getNumRecords() < rows.size() ?
select.getNumRecords() : rows.size());
}
private static List<org.apache.cassandra.db.Row> getIndexedSlices(String
keyspace, SelectStatement select)
Modified:
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java?rev=1100657&r1=1100656&r2=1100657&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
(original)
+++
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
Sat May 7 23:16:15 2011
@@ -126,7 +126,17 @@ public class SelectStatement
{
return isCountOper;
}
-
+
+ public boolean includeStartKey()
+ {
+ return clause.includeStartKey();
+ }
+
+ public boolean includeFinishKey()
+ {
+ return clause.includeFinishKey();
+ }
+
public AbstractType getComparator(String keyspace)
{
return DatabaseDescriptor.getComparator(keyspace, columnFamily);
Modified:
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java?rev=1100657&r1=1100656&r2=1100657&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
(original)
+++
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
Sat May 7 23:16:15 2011
@@ -33,7 +33,8 @@ public class WhereClause
private List<Term> keys = new ArrayList<Term>();
private Term startKey, finishKey;
private List<Relation> columns = new ArrayList<Relation>();
-
+ private boolean includeStartKey = false, includeFinishKey = false;
+
/**
* Create a new WhereClause with the first parsed relation.
*
@@ -61,9 +62,15 @@ public class WhereClause
if (relation.operator().equals(RelationType.EQ))
keys.add(relation.getValue());
else if ((relation.operator().equals(RelationType.GT) ||
relation.operator().equals(RelationType.GTE)))
+ {
startKey = relation.getValue();
+ includeStartKey = relation.operator().equals(RelationType.GTE);
+ }
else if ((relation.operator().equals(RelationType.LT) ||
relation.operator().equals(RelationType.LTE)))
+ {
finishKey = relation.getValue();
+ includeFinishKey =
relation.operator().equals(RelationType.LTE);
+ }
}
else
@@ -99,4 +106,14 @@ public class WhereClause
{
return keys;
}
+
+ public boolean includeStartKey()
+ {
+ return includeStartKey;
+ }
+
+ public boolean includeFinishKey()
+ {
+ return includeFinishKey;
+ }
}
Modified: cassandra/branches/cassandra-0.8/test/system/test_cql.py
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/test/system/test_cql.py?rev=1100657&r1=1100656&r2=1100657&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/test/system/test_cql.py (original)
+++ cassandra/branches/cassandra-0.8/test/system/test_cql.py Sat May 7
23:16:15 2011
@@ -152,29 +152,68 @@ class TestCql(ThriftTester):
def test_select_row_range(self):
"retrieve a range of rows with columns"
cursor = init()
- cursor.execute("""
- SELECT 4 FROM StandardLongA WHERE KEY > 'ad' AND KEY < 'ag';
- """)
- rows = [row[0] for row in cursor.fetchall()]
- assert ['ad', 'ae', 'af', 'ag'] == rows, rows
- def test_select_row_range_with_limit(self):
- "retrieve a limited range of rows with columns"
- cursor = init()
- cursor.execute("""
- SELECT 1,5,9 FROM StandardLongA WHERE KEY > 'aa'
- AND KEY < 'ag' LIMIT 3
- """)
- assert cursor.rowcount == 3
-
- cursor.execute("""
- SELECT 20,40 FROM StandardIntegerA WHERE KEY > 'k1'
- AND KEY < 'k7' LIMIT 5
- """)
- assert cursor.rowcount == 5
- for i in range(5):
- r = cursor.fetchone()
- assert r[0] == "k%d" % (i+1)
+ # everything
+ cursor.execute("SELECT * FROM StandardLongA")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'] == keys, keys
+
+ # [start, end], mid-row
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY >= 'ad' AND KEY
<= 'ag'")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['ad', 'ae', 'af', 'ag'] == keys, keys
+
+ # (start, end), mid-row
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'ad' AND KEY <
'ag'")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['ae', 'af'] == keys, keys
+
+ # [start, end], full-row
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY >= 'aa' AND KEY
<= 'ag'")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'] == keys, keys
+
+ # (start, end), full-row
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'a' AND KEY <
'g'")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'] == keys, keys
+
+ # LIMIT tests
+
+ # no WHERE
+ cursor.execute("SELECT * FROM StandardLongA LIMIT 1")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa'] == keys, keys
+
+ # with >=, non-existing key
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY >= 'a' LIMIT 1")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa'] == keys, keys
+
+ # with >=, existing key
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY >= 'aa' LIMIT 1")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa'] == keys, keys
+
+ # with >, non-existing key
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'a' LIMIT 1")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa'] == keys, keys
+
+ # with >, existing key
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'aa' LIMIT 1")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['ab'] == keys, keys
+
+ # with both > and <, existing keys
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'aa' and KEY <
'ag' LIMIT 5")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['ab', 'ac', 'ad', 'ae', 'af'] == keys, keys
+
+ # with both > and <, non-existing keys
+ cursor.execute("SELECT * FROM StandardLongA WHERE KEY > 'a' and KEY <
'b' LIMIT 5")
+ keys = [row[0] for row in cursor.fetchall()]
+ assert ['aa', 'ab', 'ac', 'ad', 'ae'] == keys, keys
def test_select_columns_slice(self):
"column slice tests"
@@ -294,7 +333,7 @@ class TestCql(ThriftTester):
cursor = init()
cursor.execute("""
SELECT 'birthdate' FROM IndexedA WHERE 'birthdate' = 100
- AND KEY > 'asmithZ'
+ AND KEY >= 'asmithZ'
""")
assert cursor.rowcount == 1
r = cursor.fetchone()