Repository: cassandra Updated Branches: refs/heads/trunk 5295f763d -> 9e7489116
Add support for secondary indexes on static columns Patch by Taiyuan Zhang; reviewed by Sam Tunnicliffe for CASSANDRA-8103 Also includes fix for CASSANDRA-10958 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9e748911 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9e748911 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9e748911 Branch: refs/heads/trunk Commit: 9e7489116feace284e0d704135186991a67ee5e3 Parents: 5295f76 Author: Taiyuan Zhang <[email protected]> Authored: Mon Jan 18 23:57:59 2016 -0800 Committer: Sam Tunnicliffe <[email protected]> Committed: Wed Jan 20 09:58:28 2016 +0000 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../restrictions/StatementRestrictions.java | 17 ++ .../cql3/statements/CreateIndexStatement.java | 9 - .../cql3/statements/SelectStatement.java | 10 +- .../apache/cassandra/db/filter/RowFilter.java | 5 + .../index/internal/CassandraIndex.java | 1 + .../composites/CollectionKeyIndexBase.java | 21 +- .../composites/CollectionValueIndex.java | 20 +- .../internal/composites/CompositesSearcher.java | 195 ++++++++++------- .../internal/composites/RegularColumnIndex.java | 22 +- .../unit/org/apache/cassandra/SchemaLoader.java | 27 ++- .../apache/cassandra/cql3/SimpleQueryTest.java | 40 ++++ .../SecondaryIndexOnStaticColumnTest.java | 217 +++++++++++++++++++ .../validation/entities/SecondaryIndexTest.java | 3 - .../apache/cassandra/db/SecondaryIndexTest.java | 40 +++- 15 files changed, 510 insertions(+), 118 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 789f36e..81b3978 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.4 + * Add support for secondary indexes on static columns (CASSANDRA-8103) * CommitLogUpgradeTestMaker creates broken commit logs (CASSANDRA-11051) * Add metric for number of dropped mutations (CASSANDRA-10866) * Simplify row cache invalidation code (CASSANDRA-10396) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java index 1c7db4e..fee17c2 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -78,6 +78,12 @@ public final class StatementRestrictions */ private RestrictionSet nonPrimaryKeyRestrictions; + /** + * <code>true</code> if nonPrimaryKeyRestrictions contains restriction on a regular column, + * <code>false</code> otherwise. + */ + private boolean hasRegularColumnsRestriction = false; + private Set<ColumnDefinition> notNullColumns; /** @@ -258,7 +264,13 @@ public final class StatementRestrictions else if (def.isClusteringColumn()) clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction); else + { + if (restriction.columnDef.kind == ColumnDefinition.Kind.REGULAR) + { + hasRegularColumnsRestriction = true; + } nonPrimaryKeyRestrictions = nonPrimaryKeyRestrictions.addRestriction(restriction); + } } /** @@ -332,6 +344,11 @@ public final class StatementRestrictions return this.isKeyRange; } + public boolean hasRegularColumnsRestriction() + { + return hasRegularColumnsRestriction; + } + /** * Checks if the secondary index need to be queried. * http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/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 b2a6fd5..f3d22e9 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java @@ -105,15 +105,6 @@ public class CreateIndexStatement extends SchemaAlteringStatement if (cfm.isCompactTable() && cd.isPrimaryKeyColumn()) throw new InvalidRequestException("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables"); - // It would be possible to support 2ndary index on static columns (but not without modifications of at least ExtendedFilter and - // CompositesIndex) and maybe we should, but that means a query like: - // SELECT * FROM foo WHERE static_column = 'bar' - // would pull the full partition every time the static column of partition is 'bar', which sounds like offering a - // fair potential for foot-shooting, so I prefer leaving that to a follow up ticket once we have identified cases where - // such indexing is actually useful. - if (!cfm.isCompactTable() && cd.isStatic()) - throw new InvalidRequestException("Secondary indexes are not allowed on static columns"); - if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && cfm.getKeyValidatorAsClusteringComparator().size() == 1) throw new InvalidRequestException(String.format("Cannot create secondary index on partition key column %s", target.column)); http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/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 c196e5e..5346334 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -589,7 +589,7 @@ public class SelectStatement implements CQLStatement * Returns the limit specified by the user. * May be used by custom QueryHandler implementations * - * @return the limit specified by the user or <code>DataLimits.NO_LIMIT</code> if no value + * @return the limit specified by the user or <code>DataLimits.NO_LIMIT</code> if no value * as been specified. */ public int getLimit(QueryOptions options) @@ -681,12 +681,14 @@ public class SelectStatement implements CQLStatement ByteBuffer[] keyComponents = getComponents(cfm, partition.partitionKey()); Row staticRow = partition.staticRow(); - // If there is no rows, then provided the select was a full partition selection - // (i.e. not a 2ndary index search and there was no condition on clustering columns), + // If there is no rows, and there's no restriction on clustering/regular columns, + // then provided the select was a full partition selection (either by partition key and/or by static column), // we want to include static columns and we're done. if (!partition.hasNext()) { - if (!staticRow.isEmpty() && (!restrictions.usesSecondaryIndexing() || cfm.isStaticCompactTable()) && !restrictions.hasClusteringColumnsRestriction()) + if (!staticRow.isEmpty() + && (!restrictions.hasClusteringColumnsRestriction() || cfm.isStaticCompactTable()) + && !restrictions.hasRegularColumnsRestriction()) { result.newRow(protocolVersion); for (ColumnDefinition def : selection.getColumns()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/db/filter/RowFilter.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java b/src/java/org/apache/cassandra/db/filter/RowFilter.java index 17db323..4cd2f64 100644 --- a/src/java/org/apache/cassandra/db/filter/RowFilter.java +++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java @@ -229,6 +229,11 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression> DecoratedKey pk; public UnfilteredRowIterator applyToPartition(UnfilteredRowIterator partition) { + // The filter might be on static columns, so need to check static row first. + Row staticRow = applyToRow(partition.staticRow()); + if (staticRow == null) + return null; + pk = partition.partitionKey(); return Transformation.apply(partition, this); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/index/internal/CassandraIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/internal/CassandraIndex.java b/src/java/org/apache/cassandra/index/internal/CassandraIndex.java index ef813b8..7322611 100644 --- a/src/java/org/apache/cassandra/index/internal/CassandraIndex.java +++ b/src/java/org/apache/cassandra/index/internal/CassandraIndex.java @@ -816,6 +816,7 @@ public abstract class CassandraIndex implements Index case CLUSTERING: return CassandraIndexFunctions.CLUSTERING_COLUMN_INDEX_FUNCTIONS; case REGULAR: + case STATIC: return CassandraIndexFunctions.REGULAR_COLUMN_INDEX_FUNCTIONS; case PARTITION_KEY: return CassandraIndexFunctions.PARTITION_KEY_INDEX_FUNCTIONS; http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/index/internal/composites/CollectionKeyIndexBase.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/internal/composites/CollectionKeyIndexBase.java b/src/java/org/apache/cassandra/index/internal/composites/CollectionKeyIndexBase.java index fe77c96..ef76870 100644 --- a/src/java/org/apache/cassandra/index/internal/composites/CollectionKeyIndexBase.java +++ b/src/java/org/apache/cassandra/index/internal/composites/CollectionKeyIndexBase.java @@ -54,6 +54,9 @@ public abstract class CollectionKeyIndexBase extends CassandraIndex { CBuilder builder = CBuilder.create(getIndexComparator()); builder.add(partitionKey); + + // When indexing a static column, prefix will be empty but only the + // partition key is needed at query time. for (int i = 0; i < prefix.size(); i++) builder.add(prefix.get(i)); @@ -63,16 +66,24 @@ public abstract class CollectionKeyIndexBase extends CassandraIndex public IndexEntry decodeEntry(DecoratedKey indexedValue, Row indexEntry) { - int count = 1 + baseCfs.metadata.clusteringColumns().size(); Clustering clustering = indexEntry.clustering(); - CBuilder builder = CBuilder.create(baseCfs.getComparator()); - for (int i = 0; i < count - 1; i++) - builder.add(clustering.get(i + 1)); + + Clustering indexedEntryClustering = null; + if (getIndexedColumn().isStatic()) + indexedEntryClustering = Clustering.STATIC_CLUSTERING; + else + { + int count = 1 + baseCfs.metadata.clusteringColumns().size(); + CBuilder builder = CBuilder.create(baseCfs.getComparator()); + for (int i = 0; i < count - 1; i++) + builder.add(clustering.get(i + 1)); + indexedEntryClustering = builder.build(); + } return new IndexEntry(indexedValue, clustering, indexEntry.primaryKeyLivenessInfo().timestamp(), clustering.get(0), - builder.build()); + indexedEntryClustering); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/index/internal/composites/CollectionValueIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/internal/composites/CollectionValueIndex.java b/src/java/org/apache/cassandra/index/internal/composites/CollectionValueIndex.java index 95bd7e1..5929e69 100644 --- a/src/java/org/apache/cassandra/index/internal/composites/CollectionValueIndex.java +++ b/src/java/org/apache/cassandra/index/internal/composites/CollectionValueIndex.java @@ -63,7 +63,10 @@ public class CollectionValueIndex extends CassandraIndex for (int i = 0; i < prefix.size(); i++) builder.add(prefix.get(i)); - // When indexing, cell will be present, but when searching, it won't (CASSANDRA-7525) + // When indexing a static column, prefix will be empty but only the + // partition key is needed at query time. + // In the non-static case, cell will be present during indexing but + // not when searching (CASSANDRA-7525). if (prefix.size() == baseCfs.metadata.clusteringColumns().size() && path != null) builder.add(path.get(0)); @@ -73,15 +76,22 @@ public class CollectionValueIndex extends CassandraIndex public IndexEntry decodeEntry(DecoratedKey indexedValue, Row indexEntry) { Clustering clustering = indexEntry.clustering(); - CBuilder builder = CBuilder.create(baseCfs.getComparator()); - for (int i = 0; i < baseCfs.getComparator().size(); i++) - builder.add(clustering.get(i + 1)); + Clustering indexedEntryClustering = null; + if (getIndexedColumn().isStatic()) + indexedEntryClustering = Clustering.STATIC_CLUSTERING; + else + { + CBuilder builder = CBuilder.create(baseCfs.getComparator()); + for (int i = 0; i < baseCfs.getComparator().size(); i++) + builder.add(clustering.get(i + 1)); + indexedEntryClustering = builder.build(); + } return new IndexEntry(indexedValue, clustering, indexEntry.primaryKeyLivenessInfo().timestamp(), clustering.get(0), - builder.build()); + indexedEntryClustering); } public boolean supportsOperator(ColumnDefinition indexedColumn, Operator operator) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/index/internal/composites/CompositesSearcher.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/internal/composites/CompositesSearcher.java b/src/java/org/apache/cassandra/index/internal/composites/CompositesSearcher.java index ddaa653..c12ac6b 100644 --- a/src/java/org/apache/cassandra/index/internal/composites/CompositesSearcher.java +++ b/src/java/org/apache/cassandra/index/internal/composites/CompositesSearcher.java @@ -24,6 +24,8 @@ import java.util.List; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.db.*; import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter; +import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter; +import org.apache.cassandra.db.filter.ColumnFilter; import org.apache.cassandra.db.filter.DataLimits; import org.apache.cassandra.db.filter.RowFilter; import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator; @@ -50,6 +52,11 @@ public class CompositesSearcher extends CassandraIndexSearcher return command.selectsKey(partitionKey) && command.selectsClustering(partitionKey, entry.indexedEntryClustering); } + private boolean isStaticColumn() + { + return index.getIndexedColumn().isStatic(); + } + protected UnfilteredPartitionIterator queryDataFromIndex(final DecoratedKey indexKey, final RowIterator indexHits, final ReadCommand command, @@ -103,52 +110,68 @@ public class CompositesSearcher extends CassandraIndexSearcher nextEntry = index.decodeEntry(indexKey, indexHits.next()); } - // Gather all index hits belonging to the same partition and query the data for those hits. - // TODO: it's much more efficient to do 1 read for all hits to the same partition than doing - // 1 read per index hit. However, this basically mean materializing all hits for a partition - // in memory so we should consider adding some paging mechanism. However, index hits should - // be relatively small so it's much better than the previous code that was materializing all - // *data* for a given partition. - BTreeSet.Builder<Clustering> clusterings = BTreeSet.builder(index.baseCfs.getComparator()); - List<IndexEntry> entries = new ArrayList<>(); + SinglePartitionReadCommand dataCmd; DecoratedKey partitionKey = index.baseCfs.decorateKey(nextEntry.indexedKey); - - while (nextEntry != null && partitionKey.getKey().equals(nextEntry.indexedKey)) + List<IndexEntry> entries = new ArrayList<>(); + if (isStaticColumn()) { - // We're queried a slice of the index, but some hits may not match some of the clustering column constraints - if (isMatchingEntry(partitionKey, nextEntry, command)) - { - clusterings.add(nextEntry.indexedEntryClustering); - entries.add(nextEntry); - } - + // If the index is on a static column, we just need to do a full read on the partition. + // Note that we want to re-use the command.columnFilter() in case of future change. + dataCmd = SinglePartitionReadCommand.create(index.baseCfs.metadata, + command.nowInSec(), + command.columnFilter(), + RowFilter.NONE, + DataLimits.NONE, + partitionKey, + new ClusteringIndexSliceFilter(Slices.ALL, false)); + entries.add(nextEntry); nextEntry = indexHits.hasNext() ? index.decodeEntry(indexKey, indexHits.next()) : null; } - - // Because we've eliminated entries that don't match the clustering columns, it's possible we added nothing - if (clusterings.isEmpty()) + else { - continue; + // Gather all index hits belonging to the same partition and query the data for those hits. + // TODO: it's much more efficient to do 1 read for all hits to the same partition than doing + // 1 read per index hit. However, this basically mean materializing all hits for a partition + // in memory so we should consider adding some paging mechanism. However, index hits should + // be relatively small so it's much better than the previous code that was materializing all + // *data* for a given partition. + BTreeSet.Builder<Clustering> clusterings = BTreeSet.builder(index.baseCfs.getComparator()); + while (nextEntry != null && partitionKey.getKey().equals(nextEntry.indexedKey)) + { + // We're queried a slice of the index, but some hits may not match some of the clustering column constraints + if (isMatchingEntry(partitionKey, nextEntry, command)) + { + clusterings.add(nextEntry.indexedEntryClustering); + entries.add(nextEntry); + } + + nextEntry = indexHits.hasNext() ? index.decodeEntry(indexKey, indexHits.next()) : null; + } + + // Because we've eliminated entries that don't match the clustering columns, it's possible we added nothing + if (clusterings.isEmpty()) + continue; + + // Query the gathered index hits. We still need to filter stale hits from the resulting query. + ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(clusterings.build(), false); + dataCmd = SinglePartitionReadCommand.create(index.baseCfs.metadata, + command.nowInSec(), + command.columnFilter(), + command.rowFilter(), + DataLimits.NONE, + partitionKey, + filter); } - // Query the gathered index hits. We still need to filter stale hits from the resulting query. - ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(clusterings.build(), false); - SinglePartitionReadCommand dataCmd = SinglePartitionReadCommand.create(index.baseCfs.metadata, - command.nowInSec(), - command.columnFilter(), - command.rowFilter(), - DataLimits.NONE, - partitionKey, - filter); @SuppressWarnings("resource") // We close right away if empty, and if it's assign to next it will be called either // by the next caller of next, or through closing this iterator is this come before. - UnfilteredRowIterator dataIter = filterStaleEntries(dataCmd.queryMemtableAndDisk(index.baseCfs, - executionController.baseReadOpOrderGroup()), - indexKey.getKey(), - entries, - executionController.writeOpOrderGroup(), - command.nowInSec()); - + UnfilteredRowIterator dataIter = + filterStaleEntries(dataCmd.queryMemtableAndDisk(index.baseCfs, + executionController.baseReadOpOrderGroup()), + indexKey.getKey(), + entries, + executionController.writeOpOrderGroup(), + command.nowInSec()); if (dataIter.isEmpty()) { @@ -179,11 +202,12 @@ public class CompositesSearcher extends CassandraIndexSearcher { entries.forEach(entry -> index.deleteStaleEntry(entry.indexValue, - entry.indexClustering, - new DeletionTime(entry.timestamp, nowInSec), - writeOp)); + entry.indexClustering, + new DeletionTime(entry.timestamp, nowInSec), + writeOp)); } + // We assume all rows in dataIter belong to the same partition. private UnfilteredRowIterator filterStaleEntries(UnfilteredRowIterator dataIter, final ByteBuffer indexValue, final List<IndexEntry> entries, @@ -204,50 +228,75 @@ public class CompositesSearcher extends CassandraIndexSearcher }); } + UnfilteredRowIterator iteratorToReturn = null; ClusteringComparator comparator = dataIter.metadata().comparator; - class Transform extends Transformation + if (isStaticColumn()) { - private int entriesIdx; + if (entries.size() != 1) + throw new AssertionError("A partition should have at most one index within a static column index"); - @Override - public Row applyToRow(Row row) + iteratorToReturn = dataIter; + if (index.isStale(dataIter.staticRow(), indexValue, nowInSec)) { - IndexEntry entry = findEntry(row.clustering()); - if (!index.isStale(row, indexValue, nowInSec)) - return row; - - staleEntries.add(entry); - return null; + // The entry is staled, we return no rows in this partition. + staleEntries.addAll(entries); + iteratorToReturn = UnfilteredRowIterators.noRowsIterator(dataIter.metadata(), + dataIter.partitionKey(), + Rows.EMPTY_STATIC_ROW, + dataIter.partitionLevelDeletion(), + dataIter.isReverseOrder()); } - - private IndexEntry findEntry(Clustering clustering) + deleteAllEntries(staleEntries, writeOp, nowInSec); + } + else + { + class Transform extends Transformation { - assert entriesIdx < entries.size(); - while (entriesIdx < entries.size()) + private int entriesIdx; + + @Override + public Row applyToRow(Row row) { - IndexEntry entry = entries.get(entriesIdx++); - // The entries are in clustering order. So that the requested entry should be the - // next entry, the one at 'entriesIdx'. However, we can have stale entries, entries - // that have no corresponding row in the base table typically because of a range - // tombstone or partition level deletion. Delete such stale entries. - int cmp = comparator.compare(entry.indexedEntryClustering, clustering); - assert cmp <= 0; // this would means entries are not in clustering order, which shouldn't happen - if (cmp == 0) - return entry; - else - staleEntries.add(entry); + IndexEntry entry = findEntry(row.clustering()); + if (!index.isStale(row, indexValue, nowInSec)) + return row; + + staleEntries.add(entry); + return null; } - // entries correspond to the rows we've queried, so we shouldn't have a row that has no corresponding entry. - throw new AssertionError(); - } - @Override - public void onPartitionClose() - { - deleteAllEntries(staleEntries, writeOp, nowInSec); + private IndexEntry findEntry(Clustering clustering) + { + assert entriesIdx < entries.size(); + while (entriesIdx < entries.size()) + { + IndexEntry entry = entries.get(entriesIdx++); + // The entries are in clustering order. So that the requested entry should be the + // next entry, the one at 'entriesIdx'. However, we can have stale entries, entries + // that have no corresponding row in the base table typically because of a range + // tombstone or partition level deletion. Delete such stale entries. + // For static column, we only need to compare the partition key, otherwise we compare + // the whole clustering. + int cmp = comparator.compare(entry.indexedEntryClustering, clustering); + assert cmp <= 0; // this would means entries are not in clustering order, which shouldn't happen + if (cmp == 0) + return entry; + else + staleEntries.add(entry); + } + // entries correspond to the rows we've queried, so we shouldn't have a row that has no corresponding entry. + throw new AssertionError(); + } + + @Override + public void onPartitionClose() + { + deleteAllEntries(staleEntries, writeOp, nowInSec); + } } + iteratorToReturn = Transformation.apply(dataIter, new Transform()); } - return Transformation.apply(dataIter, new Transform()); + return iteratorToReturn; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/src/java/org/apache/cassandra/index/internal/composites/RegularColumnIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/internal/composites/RegularColumnIndex.java b/src/java/org/apache/cassandra/index/internal/composites/RegularColumnIndex.java index f1dc3af..9cbfe03 100644 --- a/src/java/org/apache/cassandra/index/internal/composites/RegularColumnIndex.java +++ b/src/java/org/apache/cassandra/index/internal/composites/RegularColumnIndex.java @@ -68,22 +68,34 @@ public class RegularColumnIndex extends CassandraIndex for (int i = 0; i < prefix.size(); i++) builder.add(prefix.get(i)); + // Note: if indexing a static column, prefix will be Clustering.STATIC_CLUSTERING + // so the Clustering obtained from builder::build will contain a value for only + // the partition key. At query time though, this is all that's needed as the entire + // base table partition should be returned for any mathching index entry. return builder; } public IndexEntry decodeEntry(DecoratedKey indexedValue, Row indexEntry) { Clustering clustering = indexEntry.clustering(); - ClusteringComparator baseComparator = baseCfs.getComparator(); - CBuilder builder = CBuilder.create(baseComparator); - for (int i = 0; i < baseComparator.size(); i++) - builder.add(clustering.get(i + 1)); + + Clustering indexedEntryClustering = null; + if (getIndexedColumn().isStatic()) + indexedEntryClustering = Clustering.STATIC_CLUSTERING; + else + { + ClusteringComparator baseComparator = baseCfs.getComparator(); + CBuilder builder = CBuilder.create(baseComparator); + for (int i = 0; i < baseComparator.size(); i++) + builder.add(clustering.get(i + 1)); + indexedEntryClustering = builder.build(); + } return new IndexEntry(indexedValue, clustering, indexEntry.primaryKeyLivenessInfo().timestamp(), clustering.get(0), - builder.build()); + indexedEntryClustering); } public boolean isStale(Row data, ByteBuffer indexValue, int nowInSec) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/test/unit/org/apache/cassandra/SchemaLoader.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java index 5d720c4..e375990 100644 --- a/test/unit/org/apache/cassandra/SchemaLoader.java +++ b/test/unit/org/apache/cassandra/SchemaLoader.java @@ -398,7 +398,12 @@ public class SchemaLoader return standardCFMD(ksName, cfName); } - public static CFMetaData compositeIndexCFMD(String ksName, String cfName, boolean withIndex) throws ConfigurationException + public static CFMetaData compositeIndexCFMD(String ksName, String cfName, boolean withRegularIndex) throws ConfigurationException + { + return compositeIndexCFMD(ksName, cfName, withRegularIndex, false); + } + + public static CFMetaData compositeIndexCFMD(String ksName, String cfName, boolean withRegularIndex, boolean withStaticIndex) throws ConfigurationException { // the withIndex flag exists to allow tests index creation // on existing columns @@ -407,9 +412,11 @@ public class SchemaLoader .addClusteringColumn("c1", AsciiType.instance) .addRegularColumn("birthdate", LongType.instance) .addRegularColumn("notbirthdate", LongType.instance) + .addStaticColumn("static", LongType.instance) .build(); - if (withIndex) + if (withRegularIndex) + { cfm.indexes( cfm.getIndexes() .with(IndexMetadata.fromIndexTargets(cfm, @@ -419,6 +426,20 @@ public class SchemaLoader "birthdate_key_index", IndexMetadata.Kind.COMPOSITES, Collections.EMPTY_MAP))); + } + + if (withStaticIndex) + { + cfm.indexes( + cfm.getIndexes() + .with(IndexMetadata.fromIndexTargets(cfm, + Collections.singletonList( + new IndexTarget(new ColumnIdentifier("static", true), + IndexTarget.Type.VALUES)), + "static_index", + IndexMetadata.Kind.COMPOSITES, + Collections.EMPTY_MAP))); + } return cfm.compression(getCompressionParameters()); } @@ -446,7 +467,7 @@ public class SchemaLoader return cfm.compression(getCompressionParameters()); } - + public static CFMetaData jdbcCFMD(String ksName, String cfName, AbstractType comp) { return CFMetaData.Builder.create(ksName, cfName).addPartitionKey("key", BytesType.instance) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/test/unit/org/apache/cassandra/cql3/SimpleQueryTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/SimpleQueryTest.java b/test/unit/org/apache/cassandra/cql3/SimpleQueryTest.java index 052b53d..a46c750 100644 --- a/test/unit/org/apache/cassandra/cql3/SimpleQueryTest.java +++ b/test/unit/org/apache/cassandra/cql3/SimpleQueryTest.java @@ -18,6 +18,7 @@ package org.apache.cassandra.cql3; import java.util.*; + import org.junit.Test; import static junit.framework.Assert.*; @@ -529,4 +530,43 @@ public class SimpleQueryTest extends CQLTester row(0, 0, 0, 0) ); } + + /** Test for Cassandra issue 10958 **/ + @Test + public void restrictionOnRegularColumnWithStaticColumnPresentTest() throws Throwable + { + createTable("CREATE TABLE %s (id int, id2 int, age int static, extra int, PRIMARY KEY(id, id2))"); + + execute("INSERT INTO %s (id, id2, age, extra) VALUES (?, ?, ?, ?)", 1, 1, 1, 1); + execute("INSERT INTO %s (id, id2, age, extra) VALUES (?, ?, ?, ?)", 2, 2, 2, 2); + execute("UPDATE %s SET age=? WHERE id=?", 3, 3); + + assertRows(execute("SELECT * FROM %s"), + row(1, 1, 1, 1), + row(2, 2, 2, 2), + row(3, null, 3, null) + ); + + assertRows(execute("SELECT * FROM %s WHERE extra > 1 ALLOW FILTERING"), + row(2, 2, 2, 2) + ); + } + + @Test + public void testRowFilteringOnStaticColumn() throws Throwable + { + createTable("CREATE TABLE %s (id int, name text, age int static, PRIMARY KEY (id, name))"); + for (int i = 0; i < 5; i++) + { + execute("INSERT INTO %s (id, name, age) VALUES (?, ?, ?)", i, "NameDoesNotMatter", i); + } + + assertInvalid("SELECT id, age FROM %s WHERE age < 1"); + assertRows(execute("SELECT id, age FROM %s WHERE age < 1 ALLOW FILTERING"), + row(0, 0)); + assertRows(execute("SELECT id, age FROM %s WHERE age > 0 AND age < 3 ALLOW FILTERING"), + row(1, 1), row(2, 2)); + assertRows(execute("SELECT id, age FROM %s WHERE age > 3 ALLOW FILTERING"), + row(4, 4)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnStaticColumnTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnStaticColumnTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnStaticColumnTest.java new file mode 100644 index 0000000..f69d8d5 --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnStaticColumnTest.java @@ -0,0 +1,217 @@ +/* + * 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.validation.entities; + +import org.junit.Test; +import org.apache.cassandra.cql3.CQLTester; + +public class SecondaryIndexOnStaticColumnTest extends CQLTester +{ + @Test + public void testSimpleStaticColumn() throws Throwable + { + createTable("CREATE TABLE %s (id int, name text, age int static, PRIMARY KEY (id, name))"); + + createIndex("CREATE INDEX static_age on %s(age)"); + int id1 = 1, id2 = 2, age1 = 24, age2 = 32; + String name1A = "Taylor", name1B = "Swift", + name2 = "Jamie"; + + execute("INSERT INTO %s (id, name, age) VALUES (?, ?, ?)", id1, name1A, age1); + execute("INSERT INTO %s (id, name, age) VALUES (?, ?, ?)", id1, name1B, age1); + execute("INSERT INTO %s (id, name, age) VALUES (?, ?, ?)", id2, name2, age2); + + assertRows(execute("SELECT id, name, age FROM %s WHERE age=?", age1), + row(id1, name1B, age1), row(id1, name1A, age1)); + assertRows(execute("SELECT id, name, age FROM %s WHERE age=?", age2), + row(id2, name2, age2)); + + // Update the rows. Validate that updated values will be reflected in the index. + int newAge1 = 40; + execute("UPDATE %s SET age = ? WHERE id = ?", newAge1, id1); + assertEmpty(execute("SELECT id, name, age FROM %s WHERE age=?", age1)); + assertRows(execute("SELECT id, name, age FROM %s WHERE age=?", newAge1), + row(id1, name1B, newAge1), row(id1, name1A, newAge1)); + execute("DELETE FROM %s WHERE id = ?", id2); + assertEmpty(execute("SELECT id, name, age FROM %s WHERE age=?", age2)); + } + + @Test + public void testIndexOnCompoundRowKey() throws Throwable + { + createTable("CREATE TABLE %s (interval text, seq int, id int, severity int static, PRIMARY KEY ((interval, seq), id) ) WITH CLUSTERING ORDER BY (id DESC)"); + + execute("CREATE INDEX ON %s (severity)"); + + execute("insert into %s (interval, seq, id , severity) values('t',1, 3, 10)"); + execute("insert into %s (interval, seq, id , severity) values('t',1, 4, 10)"); + execute("insert into %s (interval, seq, id , severity) values('t',2, 3, 10)"); + execute("insert into %s (interval, seq, id , severity) values('t',2, 4, 10)"); + execute("insert into %s (interval, seq, id , severity) values('m',1, 3, 11)"); + execute("insert into %s (interval, seq, id , severity) values('m',1, 4, 11)"); + execute("insert into %s (interval, seq, id , severity) values('m',2, 3, 11)"); + execute("insert into %s (interval, seq, id , severity) values('m',2, 4, 11)"); + + assertRows(execute("select * from %s where severity = 10 and interval = 't' and seq = 1"), + row("t", 1, 4, 10), row("t", 1, 3, 10)); + } + + @Test + public void testIndexOnCollections() throws Throwable + { + createTable("CREATE TABLE %s (k int, v int, l list<int> static, s set<text> static, m map<text, int> static, PRIMARY KEY (k, v))"); + + createIndex("CREATE INDEX ON %s (l)"); + createIndex("CREATE INDEX ON %s (s)"); + createIndex("CREATE INDEX ON %s (m)"); + createIndex("CREATE INDEX ON %s (keys(m))"); + + execute("INSERT INTO %s (k, v, l, s, m) VALUES (0, 0, [1, 2], {'a'}, {'a' : 1, 'b' : 2})"); + execute("INSERT INTO %s (k, v) VALUES (0, 1) "); + execute("INSERT INTO %s (k, v, l, s, m) VALUES (1, 0, [4, 5], {'d'}, {'b' : 1, 'c' : 4})"); + + // lists + assertRows(execute("SELECT k, v FROM %s WHERE l CONTAINS 1"), row(0, 0), row(0, 1)); + assertEmpty(execute("SELECT k, v FROM %s WHERE k = 1 AND l CONTAINS 1")); + assertRows(execute("SELECT k, v FROM %s WHERE l CONTAINS 4"), row(1, 0)); + assertEmpty(execute("SELECT k, v FROM %s WHERE l CONTAINS 6")); + + // update lists + execute("UPDATE %s SET l = l + [3] WHERE k = ?", 0); + assertRows(execute("SELECT k, v FROM %s WHERE l CONTAINS 3"), row(0, 0), row(0, 1)); + + // sets + assertRows(execute("SELECT k, v FROM %s WHERE s CONTAINS 'a'"), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE k = 0 AND s CONTAINS 'a'"), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE s CONTAINS 'd'"), row(1, 0)); + assertEmpty(execute("SELECT k, v FROM %s WHERE s CONTAINS 'e'")); + + // update sets + execute("UPDATE %s SET s = s + {'b'} WHERE k = ?", 0); + assertRows(execute("SELECT k, v FROM %s WHERE s CONTAINS 'b'"), row(0, 0), row(0, 1)); + execute("UPDATE %s SET s = s - {'a'} WHERE k = ?", 0); + assertEmpty(execute("SELECT k, v FROM %s WHERE s CONTAINS 'a'")); + + // maps + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS 1"), row(1, 0), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE k = 0 AND m CONTAINS 1"), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS 4"), row(1, 0)); + assertEmpty(execute("SELECT k, v FROM %s WHERE m CONTAINS 5")); + + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS KEY 'b'"), row(1, 0), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE k = 0 AND m CONTAINS KEY 'b'"), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS KEY 'c'"), row(1, 0)); + assertEmpty(execute("SELECT k, v FROM %s WHERE m CONTAINS KEY 'd'")); + + // update maps. + execute("UPDATE %s SET m['c'] = 5 WHERE k = 0"); + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS 5"), row(0, 0), row(0, 1)); + assertRows(execute("SELECT k, v FROM %s WHERE m CONTAINS KEY 'c'"), row(1, 0), row(0, 0), row(0, 1)); + execute("DELETE m['a'] FROM %s WHERE k = 0"); + assertEmpty(execute("SELECT k, v FROM %s WHERE m CONTAINS KEY 'a'")); + } + + @Test + public void testIndexOnFrozenCollections() throws Throwable + { + createTable("CREATE TABLE %s (k int, v int, l frozen<list<int>> static, s frozen<set<text>> static, m frozen<map<text, int>> static, PRIMARY KEY (k, v))"); + + createIndex("CREATE INDEX ON %s (FULL(l))"); + createIndex("CREATE INDEX ON %s (FULL(s))"); + createIndex("CREATE INDEX ON %s (FULL(m))"); + + execute("INSERT INTO %s (k, v, l, s, m) VALUES (0, 0, [1, 2], {'a'}, {'a' : 1, 'b' : 2})"); + execute("INSERT INTO %s (k, v) VALUES (0, 1) "); + execute("INSERT INTO %s (k, v, l, s, m) VALUES (1, 0, [4, 5], {'d'}, {'b' : 1, 'c' : 4})"); + execute("UPDATE %s SET l=[3], s={'3'}, m={'3': 3} WHERE k=3" ); + + // lists + assertRows(execute("SELECT k, v FROM %s WHERE l = [1, 2]"), row(0, 0), row(0, 1)); + assertEmpty(execute("SELECT k, v FROM %s WHERE k = 1 AND l = [1, 2]")); + assertEmpty(execute("SELECT k, v FROM %s WHERE l = [4]")); + assertRows(execute("SELECT k, v FROM %s WHERE l = [3]"), row(3, null)); + + // update lists + execute("UPDATE %s SET l = [1, 2, 3] WHERE k = ?", 0); + assertEmpty(execute("SELECT k, v FROM %s WHERE l = [1, 2]")); + assertRows(execute("SELECT k, v FROM %s WHERE l = [1, 2, 3]"), row(0, 0), row(0, 1)); + + // sets + assertRows(execute("SELECT k, v FROM %s WHERE s = {'a'}"), row(0, 0), row(0, 1)); + assertEmpty(execute("SELECT k, v FROM %s WHERE k = 1 AND s = {'a'}")); + assertEmpty(execute("SELECT k, v FROM %s WHERE s = {'b'}")); + assertRows(execute("SELECT k, v FROM %s WHERE s = {'3'}"), row(3, null)); + + // update sets + execute("UPDATE %s SET s = {'a', 'b'} WHERE k = ?", 0); + assertEmpty(execute("SELECT k, v FROM %s WHERE s = {'a'}")); + assertRows(execute("SELECT k, v FROM %s WHERE s = {'a', 'b'}"), row(0, 0), row(0, 1)); + + // maps + assertRows(execute("SELECT k, v FROM %s WHERE m = {'a' : 1, 'b' : 2}"), row(0, 0), row(0, 1)); + assertEmpty(execute("SELECT k, v FROM %s WHERE k = 1 AND m = {'a' : 1, 'b' : 2}")); + assertEmpty(execute("SELECT k, v FROM %s WHERE m = {'a' : 1, 'b' : 3}")); + assertEmpty(execute("SELECT k, v FROM %s WHERE m = {'a' : 1, 'c' : 2}")); + assertRows(execute("SELECT k, v FROM %s WHERE m = {'3': 3}"), row(3, null)); + + // update maps. + execute("UPDATE %s SET m = {'a': 2, 'b': 3} WHERE k = ?", 0); + assertEmpty(execute("SELECT k, v FROM %s WHERE m = {'a': 1, 'b': 2}")); + assertRows(execute("SELECT k, v FROM %s WHERE m = {'a': 2, 'b': 3}"), row(0, 0), row(0, 1)); + } + + @Test + public void testStaticIndexAndNonStaticIndex() throws Throwable + { + createTable("CREATE TABLE %s (id int, company text, age int static, salary int, PRIMARY KEY(id, company))"); + createIndex("CREATE INDEX on %s(age)"); + createIndex("CREATE INDEX on %s(salary)"); + + String company1 = "company1", company2 = "company2"; + + execute("INSERT INTO %s(id, company, age, salary) VALUES(?, ?, ?, ?)", 1, company1, 20, 1000); + execute("INSERT INTO %s(id, company, salary) VALUES(?, ?, ?)", 1, company2, 2000); + execute("INSERT INTO %s(id, company, age, salary) VALUES(?, ?, ?, ?)", 2, company1, 40, 2000); + + assertRows(execute("SELECT id, company, age, salary FROM %s WHERE age = 20 AND salary = 2000 ALLOW FILTERING"), + row(1, company2, 20, 2000)); + } + + @Test + public void testIndexOnUDT() throws Throwable + { + String typeName = createType("CREATE TYPE %s (street text, city text)"); + + createTable(String.format( + "CREATE TABLE %%s (id int, company text, home frozen<%s> static, price int, PRIMARY KEY(id, company))", + typeName)); + createIndex("CREATE INDEX on %s(home)"); + + String addressString = "{street: 'Centre', city: 'C'}"; + String companyName = "Random"; + + execute("INSERT INTO %s(id, company, home, price) " + + "VALUES(1, '" + companyName + "', " + addressString + ", 10000)"); + assertRows(execute("SELECT id, company FROM %s WHERE home = " + addressString), row(1, companyName)); + String newAddressString = "{street: 'Fifth', city: 'P'}"; + + execute("UPDATE %s SET home = " + newAddressString + " WHERE id = 1"); + assertEmpty(execute("SELECT id, company FROM %s WHERE home = " + addressString)); + assertRows(execute("SELECT id, company FROM %s WHERE home = " + newAddressString), row(1, companyName)); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java index 06f1987..c2a7923 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java @@ -749,9 +749,6 @@ public class SecondaryIndexTest extends CQLTester assertInvalid("CREATE INDEX ON %s (a)"); assertInvalid("CREATE INDEX ON %s (b)"); assertInvalid("CREATE INDEX ON %s (c)"); - - createTable("CREATE TABLE %s (a int, b int, c int static , PRIMARY KEY (a, b))"); - assertInvalid("CREATE INDEX ON %s (c)"); } @Test http://git-wip-us.apache.org/repos/asf/cassandra/blob/9e748911/test/unit/org/apache/cassandra/db/SecondaryIndexTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/db/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/db/SecondaryIndexTest.java index ee01a47..a037d90 100644 --- a/test/unit/org/apache/cassandra/db/SecondaryIndexTest.java +++ b/test/unit/org/apache/cassandra/db/SecondaryIndexTest.java @@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; - import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.config.ColumnDefinition; @@ -37,10 +36,8 @@ import org.apache.cassandra.cql3.statements.IndexTarget; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.db.rows.Row; -import org.apache.cassandra.db.rows.RowIterator; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.index.Index; -import org.apache.cassandra.index.internal.CassandraIndex; import org.apache.cassandra.schema.IndexMetadata; import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.utils.ByteBufferUtil; @@ -66,7 +63,7 @@ public class SecondaryIndexTest SchemaLoader.prepareServer(); SchemaLoader.createKeyspace(KEYSPACE1, KeyspaceParams.simple(1), - SchemaLoader.compositeIndexCFMD(KEYSPACE1, WITH_COMPOSITE_INDEX, true).gcGraceSeconds(0), + SchemaLoader.compositeIndexCFMD(KEYSPACE1, WITH_COMPOSITE_INDEX, true, true).gcGraceSeconds(0), SchemaLoader.compositeIndexCFMD(KEYSPACE1, COMPOSITE_INDEX_TO_BE_ADDED, false).gcGraceSeconds(0), SchemaLoader.keysIndexCFMD(KEYSPACE1, WITH_KEYS_INDEX, true).gcGraceSeconds(0)); } @@ -119,7 +116,7 @@ public class SecondaryIndexTest .build(); Index.Searcher searcher = cfs.indexManager.getBestIndexFor(rc).searcherFor(rc); - try (ReadExecutionController executionController = rc.executionController(); + try (ReadExecutionController executionController = rc.executionController(); UnfilteredPartitionIterator pi = searcher.search(executionController)) { assertTrue(pi.hasNext()); @@ -325,15 +322,30 @@ public class SecondaryIndexTest @Test public void testDeleteOfInconsistentValuesFromCompositeIndex() throws Exception { + runDeleteOfInconsistentValuesFromCompositeIndexTest(false); + } + + @Test + public void testDeleteOfInconsistentValuesFromCompositeIndexOnStaticColumn() throws Exception + { + runDeleteOfInconsistentValuesFromCompositeIndexTest(true); + } + + private void runDeleteOfInconsistentValuesFromCompositeIndexTest(boolean isStatic) throws Exception + { Keyspace keyspace = Keyspace.open(KEYSPACE1); String cfName = WITH_COMPOSITE_INDEX; ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); - ByteBuffer col = ByteBufferUtil.bytes("birthdate"); + String colName = isStatic ? "static" : "birthdate"; + ByteBuffer col = ByteBufferUtil.bytes(colName); // create a row and update the author value - new RowUpdateBuilder(cfs.metadata, 0, "k1").clustering("c").add("birthdate", 10l).build().applyUnsafe(); + RowUpdateBuilder builder = new RowUpdateBuilder(cfs.metadata, 0, "k1"); + if (!isStatic) + builder = builder.clustering("c"); + builder.add(colName, 10l).build().applyUnsafe(); // test that the index query fetches this version assertIndexedOne(cfs, col, 10l); @@ -343,9 +355,11 @@ public class SecondaryIndexTest assertIndexedOne(cfs, col, 10l); // now apply another update, but force the index update to be skipped - keyspace.apply(new RowUpdateBuilder(cfs.metadata, 1, "k1").clustering("c").add("birthdate", 20l).build(), - true, - false); + builder = new RowUpdateBuilder(cfs.metadata, 0, "k1"); + if (!isStatic) + builder = builder.clustering("c"); + builder.add(colName, 20l); + keyspace.apply(builder.build(), true, false); // Now searching the index for either the old or new value should return 0 rows // because the new value was not indexed and the old value should be ignored @@ -357,7 +371,11 @@ public class SecondaryIndexTest // now, reset back to the original value, still skipping the index update, to // make sure the value was expunged from the index when it was discovered to be inconsistent // TODO: Figure out why this is re-inserting - keyspace.apply(new RowUpdateBuilder(cfs.metadata, 2, "k1").clustering("c1").add("birthdate", 10l).build(), true, false); + builder = new RowUpdateBuilder(cfs.metadata, 2, "k1"); + if (!isStatic) + builder = builder.clustering("c"); + builder.add(colName, 10L); + keyspace.apply(builder.build(), true, false); assertIndexedNone(cfs, col, 20l); ColumnFamilyStore indexCfs = cfs.indexManager.getAllIndexColumnFamilyStores().iterator().next();
