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