/* * 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.db.index.composites; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.cassandra.utils.concurrent.OpOrder; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.db.*; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.index.AbstractSimplePerColumnSecondaryIndex; import org.apache.cassandra.db.index.SecondaryIndex; import org.apache.cassandra.db.index.SecondaryIndexManager; import org.apache.cassandra.db.index.SecondaryIndexSearcher; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.exceptions.ConfigurationException; /** * Base class for secondary indexes where composites are involved. */ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIndex { private volatile CellNameType indexComparator; protected CellNameType getIndexComparator() { // Yes, this is racy, but doing this more than once is not a big deal, we just want to avoid doing it every time // More seriously, we should fix that whole SecondaryIndex API so this can be a final and avoid all that non-sense. if (indexComparator == null) { assert columnDef != null; indexComparator = getIndexComparator(baseCfs.metadata, columnDef); } return indexComparator; } public static CompositesIndex create(ColumnDefinition cfDef) { if (cfDef.type.isCollection() && cfDef.type.isMultiCell()) { switch (((CollectionType)cfDef.type).kind) { case LIST: return new CompositesIndexOnCollectionValue(); case SET: return new CompositesIndexOnCollectionKey(); case MAP: return cfDef.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME) ? new CompositesIndexOnCollectionKey() : new CompositesIndexOnCollectionValue(); } } switch (cfDef.kind) { case CLUSTERING_COLUMN: return new CompositesIndexOnClusteringKey(); case REGULAR: return new CompositesIndexOnRegular(); case PARTITION_KEY: return new CompositesIndexOnPartitionKey(); //case COMPACT_VALUE: // return new CompositesIndexOnCompactValue(); } throw new AssertionError(); } // Check SecondaryIndex.getIndexComparator if you want to know why this is static public static CellNameType getIndexComparator(CFMetaData baseMetadata, ColumnDefinition cfDef) { if (cfDef.type.isCollection() && cfDef.type.isMultiCell()) { switch (((CollectionType)cfDef.type).kind) { case LIST: return CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef); case SET: return CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef); case MAP: return cfDef.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME) ? CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef) : CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef); } } switch (cfDef.kind) { case CLUSTERING_COLUMN: return CompositesIndexOnClusteringKey.buildIndexComparator(baseMetadata, cfDef); case REGULAR: return CompositesIndexOnRegular.buildIndexComparator(baseMetadata, cfDef); case PARTITION_KEY: return CompositesIndexOnPartitionKey.buildIndexComparator(baseMetadata, cfDef); //case COMPACT_VALUE: // return CompositesIndexOnCompactValue.buildIndexComparator(baseMetadata, cfDef); } throw new AssertionError(); } protected CellName makeIndexColumnName(ByteBuffer rowKey, Cell cell) { return getIndexComparator().create(makeIndexColumnPrefix(rowKey, cell.name()), null); } protected abstract Composite makeIndexColumnPrefix(ByteBuffer rowKey, Composite columnName); public abstract IndexedEntry decodeEntry(DecoratedKey indexedValue, Cell indexEntry); public abstract boolean isStale(IndexedEntry entry, ColumnFamily data, long now); public void delete(IndexedEntry entry, OpOrder.Group opGroup) { int localDeletionTime = (int) (System.currentTimeMillis() / 1000); ColumnFamily cfi = ArrayBackedSortedColumns.factory.create(indexCfs.metadata); cfi.addTombstone(entry.indexEntry, localDeletionTime, entry.timestamp); indexCfs.apply(entry.indexValue, cfi, SecondaryIndexManager.nullUpdater, opGroup, null); if (logger.isDebugEnabled()) logger.debug("removed index entry for cleaned-up value {}:{}", entry.indexValue, cfi); } protected AbstractType<?> getExpressionComparator() { return baseCfs.metadata.getColumnDefinitionComparator(columnDef); } public SecondaryIndexSearcher createSecondaryIndexSearcher(Set<ByteBuffer> columns) { return new CompositesSearcher(baseCfs.indexManager, columns); } public void validateOptions() throws ConfigurationException { ColumnDefinition columnDef = columnDefs.iterator().next(); Map<String, String> options = new HashMap<String, String>(columnDef.getIndexOptions()); // We used to have an option called "prefix_size" so skip it silently for backward compatibility sake. options.remove("prefix_size"); if (columnDef.type.isCollection()) { options.remove(SecondaryIndex.INDEX_VALUES_OPTION_NAME); options.remove(SecondaryIndex.INDEX_KEYS_OPTION_NAME); } if (!options.isEmpty()) throw new ConfigurationException("Unknown options provided for COMPOSITES index: " + options.keySet()); } public static class IndexedEntry { public final DecoratedKey indexValue; public final CellName indexEntry; public final long timestamp; public final ByteBuffer indexedKey; public final Composite indexedEntryPrefix; public final ByteBuffer indexedEntryCollectionKey; // may be null public IndexedEntry(DecoratedKey indexValue, CellName indexEntry, long timestamp, ByteBuffer indexedKey, Composite indexedEntryPrefix) { this(indexValue, indexEntry, timestamp, indexedKey, indexedEntryPrefix, null); } public IndexedEntry(DecoratedKey indexValue, CellName indexEntry, long timestamp, ByteBuffer indexedKey, Composite indexedEntryPrefix, ByteBuffer indexedEntryCollectionKey) { this.indexValue = indexValue; this.indexEntry = indexEntry; this.timestamp = timestamp; this.indexedKey = indexedKey; this.indexedEntryPrefix = indexedEntryPrefix; this.indexedEntryCollectionKey = indexedEntryCollectionKey; } } }