Repository: cassandra
Updated Branches:
  refs/heads/cassandra-2.1 cde78097d -> 5c364016a


Queries on compact tables can return more results than requested

patch by slebresne; reviewed by thobbs for CASSANDRA-7052


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/54bf92f3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/54bf92f3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/54bf92f3

Branch: refs/heads/cassandra-2.1
Commit: 54bf92f37d4ad1a65f106b8b2a3212e8f2516282
Parents: 4596a07
Author: Sylvain Lebresne <sylv...@datastax.com>
Authored: Mon Apr 21 12:00:19 2014 +0200
Committer: Sylvain Lebresne <sylv...@datastax.com>
Committed: Mon Apr 21 12:00:19 2014 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cql3/statements/SelectStatement.java        | 34 ++++++++++++--------
 .../cassandra/service/pager/QueryPager.java     |  2 +-
 3 files changed, 23 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/54bf92f3/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 972f65a..b976d64 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.0.8
+ * Queries on compact tables can return more rows that requested 
(CASSANDRA-7052)
 Merged from 1.2:
  * Fix batchlog to account for CF truncation records (CASSANDRA-6999)
  * Fix CQLSH parsing of functions and BLOB literals (CASSANDRA-7018)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/54bf92f3/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 56e87e8..2652b29 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -194,15 +194,16 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
         cl.validateForRead(keyspace());
 
         int limit = getLimit(variables);
+        int limitForQuery = updateLimitForQuery(limit);
         long now = System.currentTimeMillis();
         Pageable command;
         if (isKeyRange || usesSecondaryIndexing)
         {
-            command = getRangeCommand(variables, limit, now);
+            command = getRangeCommand(variables, limitForQuery, now);
         }
         else
         {
-            List<ReadCommand> commands = getSliceCommands(variables, limit, 
now);
+            List<ReadCommand> commands = getSliceCommands(variables, 
limitForQuery, now);
             command = commands == null ? null : new 
Pageable.ReadCommands(commands);
         }
 
@@ -221,7 +222,7 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
         {
             QueryPager pager = QueryPagers.pager(command, cl, 
options.getPagingState());
             if (parameters.isCount)
-                return pageCountQuery(pager, variables, pageSize, now);
+                return pageCountQuery(pager, variables, pageSize, now, limit);
 
             // We can't properly do post-query ordering if we page (see #6722)
             if (needsPostQueryOrdering())
@@ -253,7 +254,7 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
         return processResults(rows, variables, limit, now);
     }
 
-    private ResultMessage.Rows pageCountQuery(QueryPager pager, 
List<ByteBuffer> variables, int pageSize, long now) throws 
RequestValidationException, RequestExecutionException
+    private ResultMessage.Rows pageCountQuery(QueryPager pager, 
List<ByteBuffer> variables, int pageSize, long now, int limit) throws 
RequestValidationException, RequestExecutionException
     {
         int count = 0;
         while (!pager.isExhausted())
@@ -264,7 +265,9 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
             count += rset.rows.size();
         }
 
-        ResultSet result = ResultSet.makeCountResult(keyspace(), 
columnFamily(), count, parameters.countAlias);
+        // We sometimes query one more result than the user limit asks to 
handle exclusive bounds with compact tables (see updateLimitForQuery).
+        // So do make sure the count is not greater than what the user asked 
for.
+        ResultSet result = ResultSet.makeCountResult(keyspace(), 
columnFamily(), Math.min(count, limit), parameters.countAlias);
         return new ResultMessage.Rows(result);
     }
 
@@ -289,16 +292,17 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
     {
         List<ByteBuffer> variables = Collections.emptyList();
         int limit = getLimit(variables);
+        int limitForQuery = updateLimitForQuery(limit);
         long now = System.currentTimeMillis();
         List<Row> rows;
         if (isKeyRange || usesSecondaryIndexing)
         {
-            RangeSliceCommand command = getRangeCommand(variables, limit, now);
+            RangeSliceCommand command = getRangeCommand(variables, 
limitForQuery, now);
             rows = command == null ? Collections.<Row>emptyList() : 
command.executeLocally();
         }
         else
         {
-            List<ReadCommand> commands = getSliceCommands(variables, limit, 
now);
+            List<ReadCommand> commands = getSliceCommands(variables, 
limitForQuery, now);
             rows = commands == null ? Collections.<Row>emptyList() : 
readLocally(keyspace(), commands);
         }
 
@@ -561,14 +565,18 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
         if (l <= 0)
             throw new InvalidRequestException("LIMIT must be strictly 
positive");
 
-        // Internally, we don't support exclusive bounds for slices. Instead,
-        // we query one more element if necessary and exclude
-        if (sliceRestriction != null && 
(!sliceRestriction.isInclusive(Bound.START) || 
!sliceRestriction.isInclusive(Bound.END)) && l != Integer.MAX_VALUE)
-            l += 1;
-
         return l;
     }
 
+    private int updateLimitForQuery(int limit)
+    {
+        // Internally, we don't support exclusive bounds for slices. Instead, 
we query one more element if necessary
+        // and exclude it later (in processColumnFamily)
+        return sliceRestriction != null && 
(!sliceRestriction.isInclusive(Bound.START) || 
!sliceRestriction.isInclusive(Bound.END)) && limit != Integer.MAX_VALUE
+             ? limit + 1
+             : limit;
+    }
+
     private Collection<ByteBuffer> getKeys(final List<ByteBuffer> variables) 
throws InvalidRequestException
     {
         List<ByteBuffer> keys = new ArrayList<ByteBuffer>();
@@ -961,7 +969,7 @@ public class SelectStatement implements CQLStatement, 
MeasurableForPreparedCache
         if (isReversed)
             cqlRows.reverse();
 
-        // Trim result if needed to respect the limit
+        // Trim result if needed to respect the user limit
         cqlRows.trim(limit);
         return cqlRows;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/54bf92f3/src/java/org/apache/cassandra/service/pager/QueryPager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/pager/QueryPager.java 
b/src/java/org/apache/cassandra/service/pager/QueryPager.java
index 218ade3..ab2dad7 100644
--- a/src/java/org/apache/cassandra/service/pager/QueryPager.java
+++ b/src/java/org/apache/cassandra/service/pager/QueryPager.java
@@ -39,7 +39,7 @@ import 
org.apache.cassandra.exceptions.RequestValidationException;
  * Also, there is no guarantee that fetchPage() won't return an empty list,
  * even if isExhausted() return false (but it is guaranteed to return an empty
  * list *if* isExhausted() return true). Indeed, isExhausted() does *not*
- * trigger a query so in some (failry rare) case we might not know the paging
+ * trigger a query so in some (fairly rare) case we might not know the paging
  * is done even though it is.
  */
 public interface QueryPager

Reply via email to