Updated Branches: refs/heads/trunk 43105f7f1 -> 0da21de3e
Add ability for CQL3 to list partition keys patch by Aleksey Yeschenko and Dan Jatnieks; reviewed by Sylvain Lebresne for CASSANDRA-4536 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b362aebd Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b362aebd Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b362aebd Branch: refs/heads/trunk Commit: b362aebd304144235121350913839b9a7ba3a5b0 Parents: bfb0647 Author: Aleksey Yeschenko <alek...@apache.org> Authored: Wed Aug 21 19:09:58 2013 +0300 Committer: Aleksey Yeschenko <alek...@apache.org> Committed: Wed Aug 21 19:09:58 2013 +0300 ---------------------------------------------------------------------- CHANGES.txt | 1 + bin/cqlsh | 4 +- doc/cql3/CQL.textile | 9 ++- pylib/cqlshlib/cql3handling.py | 2 +- src/java/org/apache/cassandra/cql3/Cql.g | 6 +- .../apache/cassandra/cql3/QueryProcessor.java | 2 +- .../cql3/statements/ModificationStatement.java | 2 +- .../cql3/statements/SelectStatement.java | 63 ++++++++++++++++---- 8 files changed, 69 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 8335423..cdc79c1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,7 @@ * Use a range aware scanner for cleanup (CASSANDRA-2524) * Cleanup doesn't need to inspect sstables that contain only local data (CASSANDRA-5722) + * Add ability for CQL3 to list partition keys (CASSANDRA-4536) 2.0.0 http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/bin/cqlsh ---------------------------------------------------------------------- diff --git a/bin/cqlsh b/bin/cqlsh index 0f51c7f..b1764db 100755 --- a/bin/cqlsh +++ b/bin/cqlsh @@ -32,7 +32,7 @@ exit 1 from __future__ import with_statement description = "CQL Shell for Apache Cassandra" -version = "4.0.0" +version = "4.0.1" from StringIO import StringIO from itertools import groupby @@ -132,7 +132,7 @@ if os.path.exists(OLD_HISTORY): DEFAULT_HOST = 'localhost' DEFAULT_PORT = 9160 -DEFAULT_CQLVER = '3.1.0' +DEFAULT_CQLVER = '3.1.1' DEFAULT_TRANSPORT_FACTORY = 'cqlshlib.tfactory.regular_transport_factory' DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S%z' http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/doc/cql3/CQL.textile ---------------------------------------------------------------------- diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile index c87b9a5..e8f991b 100644 --- a/doc/cql3/CQL.textile +++ b/doc/cql3/CQL.textile @@ -1,6 +1,6 @@ <link rel="StyleSheet" href="CQL.css" type="text/css" media="screen"> -h1. Cassandra Query Language (CQL) v3.1.0 +h1. Cassandra Query Language (CQL) v3.1.1 <span id="tableOfContents"> @@ -604,7 +604,7 @@ bc(syntax).. ( LIMIT <integer> )? ( ALLOW FILTERING )? -<select-clause> ::= <selection-list> +<select-clause> ::= DISTINCT? <selection-list> | COUNT '(' ( '*' | '1' ) ')' (AS <identifier>)? <selection-list> ::= <selector> (AS <identifier>)? ( ',' <selector> (AS <identifier>)? )* @@ -1077,12 +1077,17 @@ CQL distinguishes between _reserved_ and _non-reserved_ keywords. Reserved keywo | @WHERE@ | yes | | @WITH@ | yes | | @WRITETIME@ | no | +| @DISTINCT@ | no | h2(#changes). Changes The following describes the addition/changes brought for each version of CQL. +h3. 3.1.1 + +* @SELECT@ statement now allows listing the partition keys (using the @DISTINCT@ modifier). See "CASSANDRA-4536":https://issues.apache.org/jira/browse/CASSANDRA-4536. + h3. 3.1.0 * "ALTER TABLE":#alterTableStmt @DROP@ option has been reenabled for CQL3 tables and has new semantics now: the space formerly used by dropped columns will now be eventually reclaimed (post-compaction). You should not readd previously dropped columns unless you use timestamps with microsecond precision (see "CASSANDRA-3919":https://issues.apache.org/jira/browse/CASSANDRA-3919 for more details). http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index eacc433..0b38d04 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -568,7 +568,7 @@ syntax_rules += r''' ")" ("=" | "<" | ">" | "<=" | ">=") <tokenDefinition> | [rel_lhs]=<cident> "IN" "(" <term> ( "," <term> )* ")" ; -<selectClause> ::= <selector> ("AS" <cident>)? ("," <selector> ("AS" <cident>)?)* +<selectClause> ::= "DISTINCT"? <selector> ("AS" <cident>)? ("," <selector> ("AS" <cident>)?)* | "*" | "COUNT" "(" star=( "*" | "1" ) ")" ("AS" <cident>)? ; http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index 0035c41..a46927f 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -212,13 +212,14 @@ useStatement returns [UseStatement stmt] */ selectStatement returns [SelectStatement.RawStatement expr] @init { + boolean isDistinct = false; boolean isCount = false; ColumnIdentifier countAlias = null; Term.Raw limit = null; Map<ColumnIdentifier, Boolean> orderings = new LinkedHashMap<ColumnIdentifier, Boolean>(); boolean allowFiltering = false; } - : K_SELECT ( sclause=selectClause + : K_SELECT ( ( K_DISTINCT { isDistinct = true; } )? sclause=selectClause | (K_COUNT '(' sclause=selectCountClause ')' { isCount = true; } (K_AS c=cident { countAlias = c; })?) ) K_FROM cf=columnFamilyName ( K_WHERE wclause=whereClause )? @@ -227,6 +228,7 @@ selectStatement returns [SelectStatement.RawStatement expr] ( K_ALLOW K_FILTERING { allowFiltering = true; } )? { SelectStatement.Parameters params = new SelectStatement.Parameters(orderings, + isDistinct, isCount, countAlias, allowFiltering); @@ -939,6 +941,7 @@ unreserved_function_keyword returns [String str] | K_EXISTS | K_CUSTOM | K_TRIGGER + | K_DISTINCT ) { $str = $k.text; } | t=native_type { $str = t.toString(); } ; @@ -957,6 +960,7 @@ K_WITH: W I T H; K_LIMIT: L I M I T; K_USING: U S I N G; K_USE: U S E; +K_DISTINCT: D I S T I N C T; K_COUNT: C O U N T; K_SET: S E T; K_BEGIN: B E G I N; http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/src/java/org/apache/cassandra/cql3/QueryProcessor.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java index e593289..6d48d2d 100644 --- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java @@ -38,7 +38,7 @@ import org.apache.cassandra.utils.SemanticVersion; public class QueryProcessor { - public static final SemanticVersion CQL_VERSION = new SemanticVersion("3.1.0"); + public static final SemanticVersion CQL_VERSION = new SemanticVersion("3.1.1"); private static final Logger logger = LoggerFactory.getLogger(QueryProcessor.class); http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index e9141f9..2fce619 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -438,7 +438,7 @@ public abstract class ModificationStatement implements CQLStatement long now = System.currentTimeMillis(); Selection.ResultSetBuilder builder = selection.resultSetBuilder(now); - SelectStatement.forSelection(cfDef, selection).processColumnFamily(key, cf, Collections.<ByteBuffer>emptyList(), Integer.MAX_VALUE, now, builder); + SelectStatement.forSelection(cfDef, selection).processColumnFamily(key, cf, Collections.<ByteBuffer>emptyList(), now, builder); return builder.build(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b362aebd/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 2ac94df..8e09fe5 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.statements; import java.nio.ByteBuffer; import java.util.*; +import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Iterables; @@ -80,7 +81,7 @@ public class SelectStatement implements CQLStatement private Map<CFDefinition.Name, Integer> orderingIndexes; // Used by forSelection below - private static final Parameters defaultParameters = new Parameters(Collections.<ColumnIdentifier, Boolean>emptyMap(), false, null, false); + private static final Parameters defaultParameters = new Parameters(Collections.<ColumnIdentifier, Boolean>emptyMap(), false, false, null, false); private static enum Bound { @@ -306,7 +307,7 @@ public class SelectStatement implements CQLStatement AbstractBounds<RowPosition> keyBounds = getKeyBounds(variables); return keyBounds == null ? null - : new RangeSliceCommand(keyspace(), columnFamily(), now, filter, keyBounds, expressions, limit, true, false); + : new RangeSliceCommand(keyspace(), columnFamily(), now, filter, keyBounds, expressions, limit, !parameters.isDistinct, false); } private AbstractBounds<RowPosition> getKeyBounds(List<ByteBuffer> variables) throws InvalidRequestException @@ -369,7 +370,11 @@ public class SelectStatement implements CQLStatement private IDiskAtomFilter makeFilter(List<ByteBuffer> variables, int limit) throws InvalidRequestException { - if (isColumnRange()) + if (parameters.isDistinct) + { + return new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, false, 1, -1); + } + else if (isColumnRange()) { // For sparse, we used to ask for 'defined columns' * 'asked limit' (where defined columns includes the row marker) // to account for the grouping of columns. @@ -826,7 +831,7 @@ public class SelectStatement implements CQLStatement if (row.cf == null) continue; - processColumnFamily(row.key.key, row.cf, variables, limit, now, result); + processColumnFamily(row.key.key, row.cf, variables, now, result); } ResultSet cqlRows = result.build(); @@ -843,13 +848,24 @@ public class SelectStatement implements CQLStatement } // Used by ModificationStatement for CAS operations - void processColumnFamily(ByteBuffer key, ColumnFamily cf, List<ByteBuffer> variables, int limit, long now, Selection.ResultSetBuilder result) throws InvalidRequestException + void processColumnFamily(ByteBuffer key, ColumnFamily cf, List<ByteBuffer> variables, long now, Selection.ResultSetBuilder result) + throws InvalidRequestException { ByteBuffer[] keyComponents = cfDef.hasCompositeKey ? ((CompositeType)cfDef.cfm.getKeyValidator()).split(key) : new ByteBuffer[]{ key }; - if (cfDef.isCompact) + if (parameters.isDistinct) + { + if (!cf.hasOnlyTombstones(now)) + { + result.newRow(); + // selection.getColumnsList() will contain only the partition key components - all of them. + for (CFDefinition.Name name : selection.getColumnsList()) + result.add(keyComponents[name.position]); + } + } + else if (cfDef.isCompact) { // One cqlRow per column for (Column c : columnsInOrder(cf, variables)) @@ -1055,6 +1071,9 @@ public class SelectStatement implements CQLStatement ? Selection.wildcard(cfDef) : Selection.fromSelectors(cfDef, selectClause); + if (parameters.isDistinct) + validateDistinctSelection(selection.getColumnsList(), cfDef.keys.values()); + Term prepLimit = null; if (limit != null) { @@ -1370,6 +1389,18 @@ public class SelectStatement implements CQLStatement return new ParsedStatement.Prepared(stmt, Arrays.<ColumnSpecification>asList(names)); } + private void validateDistinctSelection(Collection<CFDefinition.Name> requestedColumns, Collection<CFDefinition.Name> partitionKey) + throws InvalidRequestException + { + for (CFDefinition.Name name : requestedColumns) + if (!partitionKey.contains(name)) + throw new InvalidRequestException(String.format("SELECT DISTINCT queries must only request partition key columns (not %s)", name)); + + for (CFDefinition.Name name : partitionKey) + if (!requestedColumns.contains(name)) + throw new InvalidRequestException(String.format("SELECT DISTINCT queries must request all the partition key columns (missing %s)", name)); + } + private boolean containsAlias(final ColumnIdentifier name) { return Iterables.any(selectClause, new Predicate<RawSelector>() @@ -1443,11 +1474,13 @@ public class SelectStatement implements CQLStatement @Override public String toString() { - return String.format("SelectRawStatement[name=%s, selectClause=%s, whereClause=%s, isCount=%s]", - cfName, - selectClause, - whereClause, - parameters.isCount); + return Objects.toStringHelper(this) + .add("name", cfName) + .add("selectClause", selectClause) + .add("whereClause", whereClause) + .add("isDistinct", parameters.isDistinct) + .add("isCount", parameters.isCount) + .toString(); } } @@ -1592,13 +1625,19 @@ public class SelectStatement implements CQLStatement public static class Parameters { private final Map<ColumnIdentifier, Boolean> orderings; + private final boolean isDistinct; private final boolean isCount; private final ColumnIdentifier countAlias; private final boolean allowFiltering; - public Parameters(Map<ColumnIdentifier, Boolean> orderings, boolean isCount, ColumnIdentifier countAlias, boolean allowFiltering) + public Parameters(Map<ColumnIdentifier, Boolean> orderings, + boolean isDistinct, + boolean isCount, + ColumnIdentifier countAlias, + boolean allowFiltering) { this.orderings = orderings; + this.isDistinct = isDistinct; this.isCount = isCount; this.countAlias = countAlias; this.allowFiltering = allowFiltering;