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