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

import java.util.*;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;

import org.apache.cassandra.config.*;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.cql3.SuperColumnCompatibility;
import org.apache.cassandra.cql3.statements.IndexTarget;
import org.apache.cassandra.db.CompactTables;
import org.apache.cassandra.db.LegacyLayout;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.db.compaction.AbstractCompactionStrategy;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.index.TargetParser;
import org.apache.cassandra.io.compress.ICompressor;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.LocalStrategy;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.UUIDGen;

Static utility methods to convert internal structure to and from thrift ones.
/** * Static utility methods to convert internal structure to and from thrift ones. */
public class ThriftConversion { public static org.apache.cassandra.db.ConsistencyLevel fromThrift(ConsistencyLevel cl) { switch (cl) { case ANY: return org.apache.cassandra.db.ConsistencyLevel.ANY; case ONE: return org.apache.cassandra.db.ConsistencyLevel.ONE; case TWO: return org.apache.cassandra.db.ConsistencyLevel.TWO; case THREE: return org.apache.cassandra.db.ConsistencyLevel.THREE; case QUORUM: return org.apache.cassandra.db.ConsistencyLevel.QUORUM; case ALL: return org.apache.cassandra.db.ConsistencyLevel.ALL; case LOCAL_QUORUM: return org.apache.cassandra.db.ConsistencyLevel.LOCAL_QUORUM; case EACH_QUORUM: return org.apache.cassandra.db.ConsistencyLevel.EACH_QUORUM; case SERIAL: return org.apache.cassandra.db.ConsistencyLevel.SERIAL; case LOCAL_SERIAL: return org.apache.cassandra.db.ConsistencyLevel.LOCAL_SERIAL; case LOCAL_ONE: return org.apache.cassandra.db.ConsistencyLevel.LOCAL_ONE; } throw new AssertionError(); } public static ConsistencyLevel toThrift(org.apache.cassandra.db.ConsistencyLevel cl) { switch (cl) { case ANY: return ConsistencyLevel.ANY; case ONE: return ConsistencyLevel.ONE; case TWO: return ConsistencyLevel.TWO; case THREE: return ConsistencyLevel.THREE; case QUORUM: return ConsistencyLevel.QUORUM; case ALL: return ConsistencyLevel.ALL; case LOCAL_QUORUM: return ConsistencyLevel.LOCAL_QUORUM; case EACH_QUORUM: return ConsistencyLevel.EACH_QUORUM; case SERIAL: return ConsistencyLevel.SERIAL; case LOCAL_SERIAL: return ConsistencyLevel.LOCAL_SERIAL; case LOCAL_ONE: return ConsistencyLevel.LOCAL_ONE; } throw new AssertionError(); } // We never return, but returning a RuntimeException allows to write "throw rethrow(e)" without java complaining // for methods that have a return value. public static RuntimeException rethrow(RequestExecutionException e) throws UnavailableException, TimedOutException { if (e instanceof RequestFailureException) throw toThrift((RequestFailureException)e); else if (e instanceof RequestTimeoutException) throw toThrift((RequestTimeoutException)e); else throw new UnavailableException(); } public static InvalidRequestException toThrift(RequestValidationException e) { return new InvalidRequestException(e.getMessage()); } public static UnavailableException toThrift(org.apache.cassandra.exceptions.UnavailableException e) { return new UnavailableException(); } public static AuthenticationException toThrift(org.apache.cassandra.exceptions.AuthenticationException e) { return new AuthenticationException(e.getMessage()); } public static TimedOutException toThrift(RequestTimeoutException e) { TimedOutException toe = new TimedOutException(); if (e instanceof WriteTimeoutException) { WriteTimeoutException wte = (WriteTimeoutException)e; toe.setAcknowledged_by(wte.received); if (wte.writeType == WriteType.BATCH_LOG) toe.setAcknowledged_by_batchlog(false); else if (wte.writeType == WriteType.BATCH) toe.setAcknowledged_by_batchlog(true); else if (wte.writeType == WriteType.CAS) toe.setPaxos_in_progress(true); } return toe; } // Thrift does not support RequestFailureExceptions, so we translate them into timeouts public static TimedOutException toThrift(RequestFailureException e) { return new TimedOutException(); } public static RowFilter rowFilterFromThrift(CFMetaData metadata, List<IndexExpression> exprs) { if (exprs == null || exprs.isEmpty()) return RowFilter.NONE; RowFilter converted = RowFilter.forThrift(exprs.size()); for (IndexExpression expr : exprs) converted.addThriftExpression(metadata, expr.column_name, Operator.valueOf(expr.op.name()), expr.value); return converted; } public static KeyspaceMetadata fromThrift(KsDef ksd, CFMetaData... cfDefs) throws ConfigurationException { Class<? extends AbstractReplicationStrategy> cls = AbstractReplicationStrategy.getClass(ksd.strategy_class); if (cls.equals(LocalStrategy.class)) throw new ConfigurationException("Unable to use given strategy class: LocalStrategy is reserved for internal use."); Map<String, String> replicationMap = new HashMap<>(); if (ksd.strategy_options != null) replicationMap.putAll(ksd.strategy_options); replicationMap.put(ReplicationParams.CLASS, cls.getName()); return KeyspaceMetadata.create(ksd.name, KeyspaceParams.create(ksd.durable_writes, replicationMap), Tables.of(cfDefs)); } public static KsDef toThrift(KeyspaceMetadata ksm) { List<CfDef> cfDefs = new ArrayList<>(); for (CFMetaData cfm : ksm.tables) // do not include views if (cfm.isThriftCompatible()) // Don't expose CF that cannot be correctly handle by thrift; see CASSANDRA-4377 for further details cfDefs.add(toThrift(cfm)); KsDef ksdef = new KsDef(ksm.name, ksm.params.replication.klass.getName(), cfDefs); ksdef.setStrategy_options(ksm.params.replication.options); ksdef.setDurable_writes(ksm.params.durableWrites); return ksdef; } public static CFMetaData fromThrift(CfDef cf_def) throws org.apache.cassandra.exceptions.InvalidRequestException, ConfigurationException { // This is a creation: the table is dense if it doesn't define any column_metadata boolean isDense = cf_def.column_metadata == null || cf_def.column_metadata.isEmpty(); return internalFromThrift(cf_def, true, Collections.<ColumnDefinition>emptyList(), isDense); } public static CFMetaData fromThriftForUpdate(CfDef cf_def, CFMetaData toUpdate) throws org.apache.cassandra.exceptions.InvalidRequestException, ConfigurationException { return internalFromThrift(cf_def, false, toUpdate.allColumns(), toUpdate.isDense()); } private static boolean isSuper(String thriftColumnType) throws org.apache.cassandra.exceptions.InvalidRequestException { switch (thriftColumnType.toLowerCase(Locale.ENGLISH)) { case "standard": return false; case "super": return true; default: throw new org.apache.cassandra.exceptions.InvalidRequestException("Invalid column type " + thriftColumnType); } }
Convert a thrift CfDef.

, This is used both for creation and update of CF.

Params:
  • cf_def – the thrift CfDef to convert.
  • isCreation – whether that is a new table creation or not.
  • previousCQLMetadata – if it is not a table creation, the previous definitions of the tables (which we use to preserve the CQL metadata). If it is a table creation, this will be empty.
  • isDense – whether the table is dense or not.
Returns:the converted table definition.
/** * Convert a thrift CfDef. * <p>, * This is used both for creation and update of CF. * * @param cf_def the thrift CfDef to convert. * @param isCreation whether that is a new table creation or not. * @param previousCQLMetadata if it is not a table creation, the previous * definitions of the tables (which we use to preserve the CQL metadata). * If it is a table creation, this will be empty. * @param isDense whether the table is dense or not. * * @return the converted table definition. */
private static CFMetaData internalFromThrift(CfDef cf_def, boolean isCreation, Collection<ColumnDefinition> previousCQLMetadata, boolean isDense) throws org.apache.cassandra.exceptions.InvalidRequestException, ConfigurationException { applyImplicitDefaults(cf_def); try { boolean isSuper = isSuper(cf_def.column_type); AbstractType<?> rawComparator = TypeParser.parse(cf_def.comparator_type); AbstractType<?> subComparator = isSuper ? cf_def.subcomparator_type == null ? BytesType.instance : TypeParser.parse(cf_def.subcomparator_type) : null; AbstractType<?> keyValidator = cf_def.isSetKey_validation_class() ? TypeParser.parse(cf_def.key_validation_class) : BytesType.instance; AbstractType<?> defaultValidator = TypeParser.parse(cf_def.default_validation_class); // Convert the definitions from the input CfDef List<ColumnDefinition> defs = fromThrift(cf_def.keyspace, cf_def.name, rawComparator, subComparator, cf_def.column_metadata); // Add the keyAlias if there is one, since that's a CQL metadata that thrift can actually change (for // historical reasons) boolean hasKeyAlias = cf_def.isSetKey_alias() && keyValidator != null && !(keyValidator instanceof CompositeType); if (hasKeyAlias) defs.add(ColumnDefinition.partitionKeyDef(cf_def.keyspace, cf_def.name, UTF8Type.instance.getString(cf_def.key_alias), keyValidator, 0)); // Now add any CQL metadata that we want to copy, skipping the keyAlias if there was one for (ColumnDefinition def : previousCQLMetadata) { // isPartOfCellName basically means 'is not just a CQL metadata' if (def.isPartOfCellName(false, isSuper)) continue; if (def.kind == ColumnDefinition.Kind.PARTITION_KEY && hasKeyAlias) continue; defs.add(def); } UUID cfId = Schema.instance.getId(cf_def.keyspace, cf_def.name); if (cfId == null) cfId = UUIDGen.getTimeUUID(); boolean isCompound = !isSuper && (rawComparator instanceof CompositeType); boolean isCounter = defaultValidator instanceof CounterColumnType; // If it's a thrift table creation, adds the default CQL metadata for the new table if (isCreation) { addDefaultCQLMetadata(defs, cf_def.keyspace, cf_def.name, hasKeyAlias ? null : keyValidator, rawComparator, subComparator, defaultValidator, isDense); } // We do not allow Thrift views, so we always set it to false boolean isView = false; CFMetaData newCFMD = CFMetaData.create(cf_def.keyspace, cf_def.name, cfId, isDense, isCompound, isSuper, isCounter, isView, defs, DatabaseDescriptor.getPartitioner()); // Convert any secondary indexes defined in the thrift column_metadata newCFMD.indexes(indexDefsFromThrift(newCFMD, cf_def.keyspace, cf_def.name, rawComparator, subComparator, cf_def.column_metadata)); if (cf_def.isSetGc_grace_seconds()) newCFMD.gcGraceSeconds(cf_def.gc_grace_seconds); newCFMD.compaction(compactionParamsFromThrift(cf_def)); if (cf_def.isSetBloom_filter_fp_chance()) newCFMD.bloomFilterFpChance(cf_def.bloom_filter_fp_chance); if (cf_def.isSetMemtable_flush_period_in_ms()) newCFMD.memtableFlushPeriod(cf_def.memtable_flush_period_in_ms); if (cf_def.isSetCaching() || cf_def.isSetCells_per_row_to_cache()) newCFMD.caching(cachingFromThrift(cf_def.caching, cf_def.cells_per_row_to_cache)); if (cf_def.isSetRead_repair_chance()) newCFMD.readRepairChance(cf_def.read_repair_chance); if (cf_def.isSetDefault_time_to_live()) newCFMD.defaultTimeToLive(cf_def.default_time_to_live); if (cf_def.isSetDclocal_read_repair_chance()) newCFMD.dcLocalReadRepairChance(cf_def.dclocal_read_repair_chance); if (cf_def.isSetMin_index_interval()) newCFMD.minIndexInterval(cf_def.min_index_interval); if (cf_def.isSetMax_index_interval()) newCFMD.maxIndexInterval(cf_def.max_index_interval); if (cf_def.isSetSpeculative_retry()) newCFMD.speculativeRetry(SpeculativeRetryParam.fromString(cf_def.speculative_retry)); if (cf_def.isSetTriggers()) newCFMD.triggers(triggerDefinitionsFromThrift(cf_def.triggers)); if (cf_def.isSetComment()) newCFMD.comment(cf_def.comment); if (cf_def.isSetCompression_options()) newCFMD.compression(compressionParametersFromThrift(cf_def.compression_options)); return newCFMD; } catch (SyntaxException | MarshalException e) { throw new ConfigurationException(e.getMessage()); } } @SuppressWarnings("unchecked") private static CompactionParams compactionParamsFromThrift(CfDef cf_def) { Class<? extends AbstractCompactionStrategy> klass = CFMetaData.createCompactionStrategy(cf_def.compaction_strategy); Map<String, String> options = new HashMap<>(cf_def.compaction_strategy_options); int minThreshold = cf_def.min_compaction_threshold; int maxThreshold = cf_def.max_compaction_threshold; if (CompactionParams.supportsThresholdParams(klass)) { options.putIfAbsent(CompactionParams.Option.MIN_THRESHOLD.toString(), Integer.toString(minThreshold)); options.putIfAbsent(CompactionParams.Option.MAX_THRESHOLD.toString(), Integer.toString(maxThreshold)); } return CompactionParams.create(klass, options); } private static CompressionParams compressionParametersFromThrift(Map<String, String> compression_options) { CompressionParams compressionParameter = CompressionParams.fromMap(compression_options); compressionParameter.validate(); return compressionParameter; } private static void addDefaultCQLMetadata(Collection<ColumnDefinition> defs, String ks, String cf, AbstractType<?> keyValidator, AbstractType<?> comparator, AbstractType<?> subComparator, AbstractType<?> defaultValidator, boolean isDense) { CompactTables.DefaultNames names = CompactTables.defaultNameGenerator(defs); if (keyValidator != null) { if (keyValidator instanceof CompositeType) { List<AbstractType<?>> subTypes = ((CompositeType)keyValidator).types; for (int i = 0; i < subTypes.size(); i++) defs.add(ColumnDefinition.partitionKeyDef(ks, cf, names.defaultPartitionKeyName(), subTypes.get(i), i)); } else { defs.add(ColumnDefinition.partitionKeyDef(ks, cf, names.defaultPartitionKeyName(), keyValidator, 0)); } } if (subComparator != null) { // SuperColumn tables: we use a special map to hold dynamic values within a given super column defs.add(ColumnDefinition.clusteringDef(ks, cf, names.defaultClusteringName(), comparator, 0)); defs.add(ColumnDefinition.regularDef(ks, cf, SuperColumnCompatibility.SUPER_COLUMN_MAP_COLUMN_STR, MapType.getInstance(subComparator, defaultValidator, true))); if (isDense) { defs.add(ColumnDefinition.clusteringDef(ks, cf, names.defaultClusteringName(), subComparator, 1)); defs.add(ColumnDefinition.regularDef(ks, cf, names.defaultCompactValueName(), defaultValidator)); } } else { List<AbstractType<?>> subTypes = comparator instanceof CompositeType ? ((CompositeType)comparator).types : Collections.<AbstractType<?>>singletonList(comparator); for (int i = 0; i < subTypes.size(); i++) defs.add(ColumnDefinition.clusteringDef(ks, cf, names.defaultClusteringName(), subTypes.get(i), i)); defs.add(ColumnDefinition.regularDef(ks, cf, names.defaultCompactValueName(), defaultValidator)); } } /* applies implicit defaults to cf definition. useful in updates */ @SuppressWarnings("deprecation") private static void applyImplicitDefaults(org.apache.cassandra.thrift.CfDef cf_def) { if (!cf_def.isSetComment()) cf_def.setComment(""); if (!cf_def.isSetMin_compaction_threshold()) cf_def.setMin_compaction_threshold(CompactionParams.DEFAULT_MIN_THRESHOLD); if (!cf_def.isSetMax_compaction_threshold()) cf_def.setMax_compaction_threshold(CompactionParams.DEFAULT_MAX_THRESHOLD); if (!cf_def.isSetCompaction_strategy()) cf_def.setCompaction_strategy(CompactionParams.DEFAULT.klass().getSimpleName()); if (!cf_def.isSetCompaction_strategy_options()) cf_def.setCompaction_strategy_options(Collections.emptyMap()); if (!cf_def.isSetCompression_options()) cf_def.setCompression_options(Collections.singletonMap(CompressionParams.SSTABLE_COMPRESSION, CompressionParams.DEFAULT.klass().getCanonicalName())); if (!cf_def.isSetDefault_time_to_live()) cf_def.setDefault_time_to_live(TableParams.DEFAULT_DEFAULT_TIME_TO_LIVE); if (!cf_def.isSetDclocal_read_repair_chance()) cf_def.setDclocal_read_repair_chance(TableParams.DEFAULT_DCLOCAL_READ_REPAIR_CHANCE); // if index_interval was set, use that for the min_index_interval default if (!cf_def.isSetMin_index_interval()) { if (cf_def.isSetIndex_interval()) cf_def.setMin_index_interval(cf_def.getIndex_interval()); else cf_def.setMin_index_interval(TableParams.DEFAULT_MIN_INDEX_INTERVAL); } if (!cf_def.isSetMax_index_interval()) { // ensure the max is at least as large as the min cf_def.setMax_index_interval(Math.max(cf_def.min_index_interval, TableParams.DEFAULT_MAX_INDEX_INTERVAL)); } } public static CfDef toThrift(CFMetaData cfm) { CfDef def = new CfDef(cfm.ksName, cfm.cfName); def.setColumn_type(cfm.isSuper() ? "Super" : "Standard"); if (cfm.isSuper()) { def.setComparator_type(cfm.comparator.subtype(0).toString()); def.setSubcomparator_type(cfm.thriftColumnNameType().toString()); } else { def.setComparator_type(LegacyLayout.makeLegacyComparator(cfm).toString()); } def.setComment(cfm.params.comment); def.setRead_repair_chance(cfm.params.readRepairChance); def.setDclocal_read_repair_chance(cfm.params.dcLocalReadRepairChance); def.setGc_grace_seconds(cfm.params.gcGraceSeconds); def.setDefault_validation_class(cfm.makeLegacyDefaultValidator().toString()); def.setKey_validation_class(cfm.getKeyValidator().toString()); def.setMin_compaction_threshold(cfm.params.compaction.minCompactionThreshold()); def.setMax_compaction_threshold(cfm.params.compaction.maxCompactionThreshold()); // We only return the alias if only one is set since thrift don't know about multiple key aliases if (cfm.partitionKeyColumns().size() == 1) def.setKey_alias(cfm.partitionKeyColumns().get(0).name.bytes); def.setColumn_metadata(columnDefinitionsToThrift(cfm, cfm.allColumns())); def.setCompaction_strategy(cfm.params.compaction.klass().getName()); def.setCompaction_strategy_options(cfm.params.compaction.options()); def.setCompression_options(compressionParametersToThrift(cfm.params.compression)); def.setBloom_filter_fp_chance(cfm.params.bloomFilterFpChance); def.setMin_index_interval(cfm.params.minIndexInterval); def.setMax_index_interval(cfm.params.maxIndexInterval); def.setMemtable_flush_period_in_ms(cfm.params.memtableFlushPeriodInMs); def.setCaching(toThrift(cfm.params.caching)); def.setCells_per_row_to_cache(toThriftCellsPerRow(cfm.params.caching)); def.setDefault_time_to_live(cfm.params.defaultTimeToLive); def.setSpeculative_retry(cfm.params.speculativeRetry.toString()); def.setTriggers(triggerDefinitionsToThrift(cfm.getTriggers())); return def; } public static ColumnDefinition fromThrift(String ksName, String cfName, AbstractType<?> thriftComparator, AbstractType<?> thriftSubcomparator, ColumnDef thriftColumnDef) throws SyntaxException, ConfigurationException { boolean isSuper = thriftSubcomparator != null; // For super columns, the componentIndex is 1 because the ColumnDefinition applies to the column component. AbstractType<?> comparator = thriftSubcomparator == null ? thriftComparator : thriftSubcomparator; try { comparator.validate(thriftColumnDef.name); } catch (MarshalException e) { throw new ConfigurationException(String.format("Column name %s is not valid for comparator %s", ByteBufferUtil.bytesToHex(thriftColumnDef.name), comparator)); } // In our generic layout, we store thrift defined columns as static, but this doesn't work for super columns so we // use a regular definition (and "dynamic" columns are handled in a map). ColumnDefinition.Kind kind = isSuper ? ColumnDefinition.Kind.REGULAR : ColumnDefinition.Kind.STATIC; return new ColumnDefinition(ksName, cfName, ColumnIdentifier.getInterned(ByteBufferUtil.clone(thriftColumnDef.name), comparator), TypeParser.parse(thriftColumnDef.validation_class), ColumnDefinition.NO_POSITION, kind); } private static List<ColumnDefinition> fromThrift(String ksName, String cfName, AbstractType<?> thriftComparator, AbstractType<?> thriftSubcomparator, List<ColumnDef> thriftDefs) throws SyntaxException, ConfigurationException { if (thriftDefs == null) return new ArrayList<>(); List<ColumnDefinition> defs = new ArrayList<>(thriftDefs.size()); for (ColumnDef thriftColumnDef : thriftDefs) defs.add(fromThrift(ksName, cfName, thriftComparator, thriftSubcomparator, thriftColumnDef)); return defs; } private static Indexes indexDefsFromThrift(CFMetaData cfm, String ksName, String cfName, AbstractType<?> thriftComparator, AbstractType<?> thriftSubComparator, List<ColumnDef> thriftDefs) { if (thriftDefs == null) return Indexes.none(); Set<String> indexNames = new HashSet<>(); Indexes.Builder indexes = Indexes.builder(); for (ColumnDef def : thriftDefs) { if (def.isSetIndex_type()) { ColumnDefinition column = fromThrift(ksName, cfName, thriftComparator, thriftSubComparator, def); String indexName = def.getIndex_name(); // add a generated index name if none was supplied if (Strings.isNullOrEmpty(indexName)) indexName = Indexes.getAvailableIndexName(ksName, cfName, column.name.toString()); if (indexNames.contains(indexName)) throw new ConfigurationException("Duplicate index name " + indexName); indexNames.add(indexName); Map<String, String> indexOptions = def.getIndex_options(); if (indexOptions != null && indexOptions.containsKey(IndexTarget.TARGET_OPTION_NAME)) throw new ConfigurationException("Reserved index option 'target' cannot be used"); IndexMetadata.Kind kind = IndexMetadata.Kind.valueOf(def.index_type.name()); indexes.add(IndexMetadata.fromLegacyMetadata(cfm, column, indexName, kind, indexOptions)); } } return indexes.build(); } @VisibleForTesting public static ColumnDef toThrift(CFMetaData cfMetaData, ColumnDefinition column) { ColumnDef cd = new ColumnDef(); cd.setName(ByteBufferUtil.clone(column.name.bytes)); cd.setValidation_class(column.type.toString()); // we include the index in the ColumnDef iff its targets are compatible with // pre-3.0 indexes AND it is the only index defined on the given column, that is: // * it is the only index on the column (i.e. with this column as its target) // * it has only a single target, which matches the pattern for pre-3.0 indexes // i.e. keys/values/entries/full, with exactly 1 argument that matches the // column name OR a simple column name (for indexes on non-collection columns) // n.b. it's a guess that using a pre-compiled regex and checking the group is // cheaper than compiling a new regex for each column, but as this isn't on // any hot path this hasn't been verified yet. IndexMetadata matchedIndex = null; for (IndexMetadata index : cfMetaData.getIndexes()) { Pair<ColumnDefinition, IndexTarget.Type> target = TargetParser.parse(cfMetaData, index); if (target.left.equals(column)) { // we already found an index for this column, we've no option but to // ignore both of them (and any others we've yet to find) if (matchedIndex != null) return cd; matchedIndex = index; } } if (matchedIndex != null) { cd.setIndex_type(org.apache.cassandra.thrift.IndexType.valueOf(matchedIndex.kind.name())); cd.setIndex_name(matchedIndex.name); Map<String, String> filteredOptions = Maps.filterKeys(matchedIndex.options, s -> !IndexTarget.TARGET_OPTION_NAME.equals(s)); cd.setIndex_options(filteredOptions.isEmpty() ? null : Maps.newHashMap(filteredOptions)); } return cd; } private static List<ColumnDef> columnDefinitionsToThrift(CFMetaData metadata, Collection<ColumnDefinition> columns) { List<ColumnDef> thriftDefs = new ArrayList<>(columns.size()); for (ColumnDefinition def : columns) if (def.isPartOfCellName(metadata.isCQLTable(), metadata.isSuper())) thriftDefs.add(ThriftConversion.toThrift(metadata, def)); return thriftDefs; } private static Triggers triggerDefinitionsFromThrift(List<TriggerDef> thriftDefs) { Triggers.Builder triggers = Triggers.builder(); for (TriggerDef thriftDef : thriftDefs) triggers.add(new TriggerMetadata(thriftDef.getName(), thriftDef.getOptions().get(TriggerMetadata.CLASS))); return triggers.build(); } private static List<TriggerDef> triggerDefinitionsToThrift(Triggers triggers) { List<TriggerDef> thriftDefs = new ArrayList<>(); for (TriggerMetadata def : triggers) { TriggerDef td = new TriggerDef(); td.setName(def.name); td.setOptions(Collections.singletonMap(TriggerMetadata.CLASS, def.classOption)); thriftDefs.add(td); } return thriftDefs; } @SuppressWarnings("deprecation") public static Map<String, String> compressionParametersToThrift(CompressionParams parameters) { if (!parameters.isEnabled()) return Collections.emptyMap(); Map<String, String> options = new HashMap<>(parameters.getOtherOptions()); Class<? extends ICompressor> klass = parameters.getSstableCompressor().getClass(); options.put(CompressionParams.SSTABLE_COMPRESSION, klass.getName()); options.put(CompressionParams.CHUNK_LENGTH_KB, parameters.chunkLengthInKB()); return options; } private static String toThrift(CachingParams caching) { if (caching.cacheRows() && caching.cacheKeys()) return "ALL"; if (caching.cacheRows()) return "ROWS_ONLY"; if (caching.cacheKeys()) return "KEYS_ONLY"; return "NONE"; } private static CachingParams cachingFromTrhfit(String caching) { switch (caching.toUpperCase(Locale.ENGLISH)) { case "ALL": return CachingParams.CACHE_EVERYTHING; case "ROWS_ONLY": return new CachingParams(false, Integer.MAX_VALUE); case "KEYS_ONLY": return CachingParams.CACHE_KEYS; case "NONE": return CachingParams.CACHE_NOTHING; default: throw new ConfigurationException(String.format("Invalid value %s for caching parameter", caching)); } } private static String toThriftCellsPerRow(CachingParams caching) { return caching.cacheAllRows() ? "ALL" : String.valueOf(caching.rowsPerPartitionToCache()); } private static int fromThriftCellsPerRow(String value) { return "ALL".equals(value) ? Integer.MAX_VALUE : Integer.parseInt(value); } public static CachingParams cachingFromThrift(String caching, String cellsPerRow) { boolean cacheKeys = true; int rowsPerPartitionToCache = 0; // if we get a caching string from thrift it is legacy, "ALL", "KEYS_ONLY" etc if (caching != null) { CachingParams parsed = cachingFromTrhfit(caching); cacheKeys = parsed.cacheKeys(); rowsPerPartitionToCache = parsed.rowsPerPartitionToCache(); } // if we get cells_per_row from thrift, it is either "ALL" or "<number of cells to cache>". if (cellsPerRow != null && rowsPerPartitionToCache > 0) rowsPerPartitionToCache = fromThriftCellsPerRow(cellsPerRow); return new CachingParams(cacheKeys, rowsPerPartitionToCache); } }