blob: 9da4d94261f171e2a37c66ddc36db69fa475fa04 [file] [log] [blame]
/*
* 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;
import java.util.HashSet;
import java.util.Set;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.SuperColumnCompatibility;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.EmptyType;
import org.apache.cassandra.db.marshal.UTF8Type;
/**
* Small utility methods pertaining to the encoding of COMPACT STORAGE tables.
*
* COMPACT STORAGE tables exists mainly for the sake of encoding internally thrift tables (as well as
* exposing those tables through CQL). Note that due to these constraints, the internal representation
* of compact tables does *not* correspond exactly to their CQL definition.
*
* The internal layout of such tables is such that it can encode any thrift table. That layout is as follow:
* CREATE TABLE compact (
* key [key_validation_class],
* [column_metadata_1] [type1] static,
* ...,
* [column_metadata_n] [type1] static,
* column [comparator],
* value [default_validation_class]
* PRIMARY KEY (key, column)
* )
* More specifically, the table:
* - always has a clustering column and a regular value, which are used to store the "dynamic" thrift columns name and value.
* Those are always present because we have no way to know in advance if "dynamic" columns will be inserted or not. Note
* that when declared from CQL, compact tables may not have any clustering: in that case, we still have a clustering
* defined internally, it is just ignored as far as interacting from CQL is concerned.
* - have a static column for every "static" column defined in the thrift "column_metadata". Note that when declaring a compact
* table from CQL without any clustering (but some non-PK columns), the columns ends up static internally even though they are
* not in the declaration
*
* On variation is that if the table comparator is a CompositeType, then the underlying table will have one clustering column by
* element of the CompositeType, but the rest of the layout is as above.
*
* SuperColumn families handling and detailed format description can be found in {@code SuperColumnCompatibility}.
*/
public abstract class CompactTables
{
private CompactTables() {}
public static ColumnDefinition getCompactValueColumn(PartitionColumns columns)
{
assert columns.regulars.simpleColumnCount() == 1 && columns.regulars.complexColumnCount() == 0;
return columns.regulars.getSimple(0);
}
public static AbstractType<?> columnDefinitionComparator(String kind, boolean isSuper, AbstractType<?> rawComparator, AbstractType<?> rawSubComparator)
{
if (!"regular".equals(kind))
return UTF8Type.instance;
return isSuper ? rawSubComparator : rawComparator;
}
public static boolean hasEmptyCompactValue(CFMetaData metadata)
{
return metadata.compactValueColumn().type instanceof EmptyType;
}
public static DefaultNames defaultNameGenerator(Set<String> usedNames)
{
return new DefaultNames(new HashSet<String>(usedNames));
}
public static DefaultNames defaultNameGenerator(Iterable<ColumnDefinition> defs)
{
Set<String> usedNames = new HashSet<>();
for (ColumnDefinition def : defs)
usedNames.add(def.name.toString());
return new DefaultNames(usedNames);
}
public static class DefaultNames
{
private static final String DEFAULT_PARTITION_KEY_NAME = "key";
private static final String DEFAULT_CLUSTERING_NAME = "column";
private static final String DEFAULT_COMPACT_VALUE_NAME = "value";
private final Set<String> usedNames;
private int partitionIndex = 0;
private int clusteringIndex = 1;
private int compactIndex = 0;
private DefaultNames(Set<String> usedNames)
{
this.usedNames = usedNames;
}
public String defaultPartitionKeyName()
{
while (true)
{
// For compatibility sake, we call the first alias 'key' rather than 'key1'. This
// is inconsistent with column alias, but it's probably not worth risking breaking compatibility now.
String candidate = partitionIndex == 0 ? DEFAULT_PARTITION_KEY_NAME : DEFAULT_PARTITION_KEY_NAME + (partitionIndex + 1);
++partitionIndex;
if (usedNames.add(candidate))
return candidate;
}
}
public String defaultClusteringName()
{
while (true)
{
String candidate = DEFAULT_CLUSTERING_NAME + clusteringIndex;
++clusteringIndex;
if (usedNames.add(candidate))
return candidate;
}
}
public String defaultCompactValueName()
{
while (true)
{
String candidate = compactIndex == 0 ? DEFAULT_COMPACT_VALUE_NAME : DEFAULT_COMPACT_VALUE_NAME + compactIndex;
++compactIndex;
if (usedNames.add(candidate))
return candidate;
}
}
}
}