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()


Reply via email to