diff --git a/src/java/org/apache/cassandra/cql/Cql.g b/src/java/org/apache/cassandra/cql/Cql.g index d2bb869d06..c9effb5bc7 100644 --- a/src/java/org/apache/cassandra/cql/Cql.g +++ b/src/java/org/apache/cassandra/cql/Cql.g @@ -143,6 +143,7 @@ selectExpression returns [SelectExpression expr] ( first=term { $expr = new SelectExpression(first, count, reversed); } (',' next=term { $expr.and(next); })* | start=term RANGEOP finish=term { $expr = new SelectExpression(start, finish, count, reversed); } + | '\*' { $expr = new SelectExpression(new Term(), new Term(), count, reversed); } ) ; diff --git a/src/java/org/apache/cassandra/cql/QueryProcessor.java b/src/java/org/apache/cassandra/cql/QueryProcessor.java index 4cef6969f4..67d74bd22b 100644 --- a/src/java/org/apache/cassandra/cql/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java @@ -100,15 +100,15 @@ public class QueryProcessor // ...a range (slice) of column names else { - validateColumnNames(keyspace, - select.getColumnFamily(), - select.getColumnStart().getByteBuffer(comparator), - select.getColumnFinish().getByteBuffer(comparator)); + ByteBuffer start = select.getColumnStart().getByteBuffer(comparator); + ByteBuffer finish = select.getColumnFinish().getByteBuffer(comparator); + + validateSliceRange(keyspace, select.getColumnFamily(), start, finish, select.isColumnsReversed()); commands.add(new SliceFromReadCommand(keyspace, key, queryPath, - select.getColumnStart().getByteBuffer(comparator), - select.getColumnFinish().getByteBuffer(comparator), + start, + finish, select.isColumnsReversed(), select.getColumnsLimit())); } @@ -370,9 +370,11 @@ public class QueryProcessor for (ByteBuffer name : columns) { if (name.remaining() > IColumn.MAX_NAME_LENGTH) - throw new InvalidRequestException(); + throw new InvalidRequestException(String.format("column name is too long (%s > %s)", + name.remaining(), + IColumn.MAX_NAME_LENGTH)); if (name.remaining() == 0) - throw new InvalidRequestException(); + throw new InvalidRequestException("zero-length column name"); try { comparator.validate(name); @@ -419,11 +421,36 @@ public class QueryProcessor throws InvalidRequestException { if (predicate.slice_range != null) - validateColumnNames(keyspace, columnFamily, predicate.slice_range.start, predicate.slice_range.finish); + validateSliceRange(keyspace, columnFamily, predicate.slice_range); else validateColumnNames(keyspace, columnFamily, predicate.column_names); } + private static void validateSliceRange(String keyspace, String columnFamily, SliceRange range) + throws InvalidRequestException + { + validateSliceRange(keyspace, columnFamily, range.start, range.finish, range.reversed); + } + + private static void validateSliceRange(String keyspace, String columnFamily, ByteBuffer start, ByteBuffer finish, boolean reversed) + throws InvalidRequestException + { + AbstractType comparator = ColumnFamily.getComparatorFor(keyspace, columnFamily, null); + try + { + comparator.validate(start); + comparator.validate(finish); + } + catch (MarshalException e) + { + throw new InvalidRequestException(e.getMessage()); + } + + Comparator orderedComparator = reversed ? comparator.reverseComparator: comparator; + if (start.remaining() > 0 && finish.remaining() > 0 && orderedComparator.compare(start, finish) > 0) + throw new InvalidRequestException("range finish must come after start in traversal order"); + } + // Copypasta from CassandraServer (where it is private). private static void validateSchemaAgreement() throws SchemaDisagreementException { diff --git a/test/system/test_cql.py b/test/system/test_cql.py index 03abf17617..fbb892a7b1 100644 --- a/test/system/test_cql.py +++ b/test/system/test_cql.py @@ -187,6 +187,14 @@ class TestCql(ThriftTester): assert r[0].columns[0].value == "a" assert r[0].columns[1].value == "b" assert r[0].columns[2].value == "c" + + def test_select_columns_slice_all(self): + "slice all columns in a row" + conn = init() + r = conn.execute("SELECT * FROM StandardString1 WHERE KEY = 'ka';") + assert len(r[0].columns) == 2 + r = conn.execute("SELECT ''..'' FROM StandardString1 WHERE KEY = 'ka';") + assert len(r[0].columns) == 2 def test_select_columns_slice_with_limit(self): "range of columns (slice) by row with limit"