Updated Branches: refs/heads/trunk d63d07b92 -> c2294aa21
Secondary indexing of map keys patch by slebresne; reviewed by iamaleksey for CASSANDRA-6383 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c2294aa2 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c2294aa2 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c2294aa2 Branch: refs/heads/trunk Commit: c2294aa21eb6310b6d5c05d6d9ff505f59b376c2 Parents: d63d07b Author: Sylvain Lebresne <[email protected]> Authored: Thu Dec 19 14:52:13 2013 +0100 Committer: Sylvain Lebresne <[email protected]> Committed: Wed Jan 8 14:34:19 2014 +0100 ---------------------------------------------------------------------- CHANGES.txt | 2 +- src/java/org/apache/cassandra/cql3/Cql.g | 12 +++++- .../cql3/statements/CreateIndexStatement.java | 36 ++++++++++++----- .../cassandra/cql3/statements/IndexTarget.java | 42 ++++++++++++++++++++ .../db/index/composites/CompositesIndex.java | 11 ++++- 5 files changed, 88 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 24722b8..58a0906 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,7 +15,7 @@ * User-defined types for CQL3 (CASSANDRA-5590) * Use of o.a.c.metrics in nodetool (CASSANDRA-5871, 6406) * Batch read from OTC's queue and cleanup (CASSANDRA-1632) - * Secondary index support for collections (CASSANDRA-4511) + * Secondary index support for collections (CASSANDRA-4511, 6383) * SSTable metadata(Stats.db) format change (CASSANDRA-6356) * Push composites support in the storage engine (CASSANDRA-5417, CASSANDRA-6520) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/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 f9f66df..11291b6 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -552,12 +552,18 @@ createIndexStatement returns [CreateIndexStatement expr] boolean ifNotExists = false; } : K_CREATE (K_CUSTOM { props.isCustom = true; })? K_INDEX (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? - (idxName=IDENT)? K_ON cf=columnFamilyName '(' id=cident ')' + (idxName=IDENT)? K_ON cf=columnFamilyName '(' id=indexIdent ')' (K_USING cls=STRING_LITERAL { props.customClass = $cls.text; })? (K_WITH properties[props])? { $expr = new CreateIndexStatement(cf, $idxName.text, id, props, ifNotExists); } ; +indexIdent returns [IndexTarget id] + : c=cident { $id = IndexTarget.of(c); } + | K_KEYS '(' c=cident ')' { $id = IndexTarget.keysOf(c); } + ; + + /** * CREATE TRIGGER triggerName ON columnFamily USING 'triggerClass'; */ @@ -958,7 +964,7 @@ relation[List<Relation> clauses] { $clauses.add(new Relation(name, Relation.Type.IN, marker)); } | name=cident K_IN { Relation rel = Relation.createInRelation($name.id); } '(' ( f1=term { rel.addInValue(f1); } (',' fN=term { rel.addInValue(fN); } )* )? ')' { $clauses.add(rel); } - | name=cident K_CONTAINS { Relation.Type rt = Relation.Type.CONTAINS; } /* (K_KEY { rt = Relation.Type.CONTAINS_KEY })? */ + | name=cident K_CONTAINS { Relation.Type rt = Relation.Type.CONTAINS; } (K_KEY { rt = Relation.Type.CONTAINS_KEY; })? t=term { $clauses.add(new Relation(name, rt, t)); } | '(' relation[$clauses] ')' ; @@ -1036,6 +1042,7 @@ unreserved_function_keyword returns [String str] basic_unreserved_keyword returns [String str] : k=( K_KEY + | K_KEYS | K_AS | K_CLUSTERING | K_COMPACT @@ -1070,6 +1077,7 @@ K_AS: A S; K_WHERE: W H E R E; K_AND: A N D; K_KEY: K E Y; +K_KEYS: K E Y S; K_INSERT: I N S E R T; K_UPDATE: U P D A T E; K_WITH: W I T H; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java index 56b465e..ca43d20 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java @@ -29,6 +29,8 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.config.IndexType; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.db.index.SecondaryIndex; +import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.cql3.*; import org.apache.cassandra.service.ClientState; @@ -42,19 +44,19 @@ public class CreateIndexStatement extends SchemaAlteringStatement private static final Logger logger = LoggerFactory.getLogger(CreateIndexStatement.class); private final String indexName; - private final ColumnIdentifier columnName; + private final IndexTarget target; private final IndexPropDefs properties; private final boolean ifNotExists; public CreateIndexStatement(CFName name, String indexName, - ColumnIdentifier columnName, + IndexTarget target, IndexPropDefs properties, boolean ifNotExists) { super(name); this.indexName = indexName; - this.columnName = columnName; + this.target = target; this.properties = properties; this.ifNotExists = ifNotExists; } @@ -70,13 +72,27 @@ public class CreateIndexStatement extends SchemaAlteringStatement if (cfm.getDefaultValidator().isCommutative()) throw new InvalidRequestException("Secondary indexes are not supported on counter tables"); - ColumnDefinition cd = cfm.getColumnDefinition(columnName); + ColumnDefinition cd = cfm.getColumnDefinition(target.column); if (cd == null) - throw new InvalidRequestException("No column definition found for column " + columnName); + throw new InvalidRequestException("No column definition found for column " + target.column); + + boolean isMap = cd.type instanceof MapType; + if (target.isCollectionKeys && !isMap) + throw new InvalidRequestException("Cannot create index on keys of column " + target + " with non map type"); if (cd.getIndexType() != null) { + boolean previousIsKeys = cd.getIndexOptions().containsKey("index_keys"); + if (isMap && target.isCollectionKeys != previousIsKeys) + { + String msg = "Cannot create index on %s %s, an index on %s %s already exists and indexing " + + "a map on both keys and values at the same time is not currently supported"; + throw new InvalidRequestException(String.format(msg, + target.column, target.isCollectionKeys ? "keys" : "values", + target.column, previousIsKeys ? "keys" : "values")); + } + if (ifNotExists) return; else @@ -87,17 +103,17 @@ public class CreateIndexStatement extends SchemaAlteringStatement // TODO: we could lift that limitation if (cfm.comparator.isDense() && cd.kind != ColumnDefinition.Kind.REGULAR) - throw new InvalidRequestException(String.format("Secondary index on %s column %s is not yet supported for compact table", cd.kind, columnName)); + throw new InvalidRequestException(String.format("Secondary index on %s column %s is not yet supported for compact table", cd.kind, target.column)); if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && cd.isOnAllComponents()) - throw new InvalidRequestException(String.format("Cannot add secondary index to already primarily indexed column %s", columnName)); + throw new InvalidRequestException(String.format("Cannot add secondary index to already primarily indexed column %s", target.column)); } public void announceMigration() throws RequestValidationException { - logger.debug("Updating column {} definition for index {}", columnName, indexName); + logger.debug("Updating column {} definition for index {}", target.column, indexName); CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).clone(); - ColumnDefinition cd = cfm.getColumnDefinition(columnName); + ColumnDefinition cd = cfm.getColumnDefinition(target.column); if (cd.getIndexType() != null && ifNotExists) return; @@ -113,7 +129,7 @@ public class CreateIndexStatement extends SchemaAlteringStatement // to also index map keys, so we record that this is the values we index to make our // lives easier then. if (cd.type.isCollection()) - options = ImmutableMap.of("index_values", ""); + options = ImmutableMap.of(target.isCollectionKeys ? "index_keys" : "index_values", ""); cd.setIndexType(IndexType.COMPOSITES, options); } else http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java new file mode 100644 index 0000000..58a8c92 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.cql3.statements; + +import org.apache.cassandra.cql3.ColumnIdentifier; + +public class IndexTarget +{ + public final ColumnIdentifier column; + public final boolean isCollectionKeys; + + private IndexTarget(ColumnIdentifier column, boolean isCollectionKeys) + { + this.column = column; + this.isCollectionKeys = isCollectionKeys; + } + + public static IndexTarget of(ColumnIdentifier c) + { + return new IndexTarget(c, false); + } + + public static IndexTarget keysOf(ColumnIdentifier c) + { + return new IndexTarget(c, true); + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java index 839df26..c92f653 100644 --- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java +++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java @@ -65,7 +65,9 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn case SET: return new CompositesIndexOnCollectionKey(); case MAP: - return new CompositesIndexOnCollectionValue(); + return cfDef.getIndexOptions().containsKey("index_keys") + ? new CompositesIndexOnCollectionKey() + : new CompositesIndexOnCollectionValue(); } } @@ -95,7 +97,9 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn case SET: return CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef); case MAP: - return CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef); + return cfDef.getIndexOptions().containsKey("index_keys") + ? CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef) + : CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef); } } @@ -154,7 +158,10 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn options.remove("prefix_size"); if (columnDef.type.isCollection()) + { options.remove("index_values"); + options.remove("index_keys"); + } if (!options.isEmpty()) throw new ConfigurationException("Unknown options provided for COMPOSITES index: " + options.keySet());
