/*
* 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;
}
}
}