Repository: cassandra Updated Branches: refs/heads/trunk 4991ca26a -> e75c51719
renamed ColumnFamilyStoreCQLHelper to TableCQLHelper Patch by Venkata+Harikrishna, reviewed by Jon Haddad for CASSANDRA-14354 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e75c5171 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e75c5171 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e75c5171 Branch: refs/heads/trunk Commit: e75c5171964b3211776136c50f0d8514b85d6295 Parents: 4991ca2 Author: Venkata+Harikrishna Nukala <[email protected]> Authored: Sat Mar 31 04:16:27 2018 +0530 Committer: Jon Haddad <[email protected]> Committed: Tue Apr 10 16:29:14 2018 -0700 ---------------------------------------------------------------------- CHANGES.txt | 2 + .../apache/cassandra/db/ColumnFamilyStore.java | 2 +- .../db/ColumnFamilyStoreCQLHelper.java | 428 ------------------ .../org/apache/cassandra/db/TableCQLHelper.java | 428 ++++++++++++++++++ .../db/ColumnFamilyStoreCQLHelperTest.java | 447 ------------------- .../apache/cassandra/db/TableCQLHelperTest.java | 447 +++++++++++++++++++ 6 files changed, 878 insertions(+), 876 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 650f740..bb8c731 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,6 @@ 4.0 + * Rename internals to reflect CQL vocabulary + (CASSANDRA-14354) * Add support for hybrid MIN(), MAX() speculative retry policies (CASSANDRA-14293, CASSANDRA-14338, CASSANDRA-14352) * Fix some regressions caused by 14058 (CASSANDRA-14353) http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/src/java/org/apache/cassandra/db/ColumnFamilyStore.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index e4b84fe..34535e5 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -1824,7 +1824,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean try (PrintStream out = new PrintStream(schemaFile)) { - for (String s: ColumnFamilyStoreCQLHelper.dumpReCreateStatements(metadata())) + for (String s: TableCQLHelper.dumpReCreateStatements(metadata())) out.println(s); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java b/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java deleted file mode 100644 index 740ef3f..0000000 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * 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.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Iterables; - -import org.apache.cassandra.cql3.statements.*; -import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.schema.*; -import org.apache.cassandra.utils.*; - -/** - * Helper methods to represent TableMetadata and related objects in CQL format - */ -public class ColumnFamilyStoreCQLHelper -{ - public static List<String> dumpReCreateStatements(TableMetadata metadata) - { - List<String> l = new ArrayList<>(); - // Types come first, as table can't be created without them - l.addAll(ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(metadata)); - // Record re-create schema statements - l.add(ColumnFamilyStoreCQLHelper.getTableMetadataAsCQL(metadata, true)); - // Dropped columns (and re-additions) - l.addAll(ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(metadata)); - // Indexes applied as last, since otherwise they may interfere with column drops / re-additions - l.addAll(ColumnFamilyStoreCQLHelper.getIndexesAsCQL(metadata)); - return l; - } - - private static List<ColumnMetadata> getClusteringColumns(TableMetadata metadata) - { - List<ColumnMetadata> cds = new ArrayList<>(metadata.clusteringColumns().size()); - - if (!metadata.isStaticCompactTable()) - for (ColumnMetadata cd : metadata.clusteringColumns()) - cds.add(cd); - - return cds; - } - - private static List<ColumnMetadata> getPartitionColumns(TableMetadata metadata) - { - List<ColumnMetadata> cds = new ArrayList<>(metadata.regularAndStaticColumns().size()); - - for (ColumnMetadata cd : metadata.staticColumns()) - cds.add(cd); - - if (metadata.isDense()) - { - // remove an empty type - for (ColumnMetadata cd : metadata.regularColumns()) - if (!cd.type.equals(EmptyType.instance)) - cds.add(cd); - } - // "regular" columns are not exposed for static compact tables - else if (!metadata.isStaticCompactTable()) - { - for (ColumnMetadata cd : metadata.regularColumns()) - cds.add(cd); - } - - return cds; - } - - /** - * Build a CQL String representation of Column Family Metadata - */ - @VisibleForTesting - public static String getTableMetadataAsCQL(TableMetadata metadata, boolean includeDroppedColumns) - { - StringBuilder sb = new StringBuilder(); - if (!isCqlCompatible(metadata)) - { - sb.append(String.format("/*\nWarning: Table %s omitted because it has constructs not compatible with CQL (was created via legacy API).\n", - metadata.toString())); - sb.append("\nApproximate structure, for reference:"); - sb.append("\n(this should not be used to reproduce this schema)\n\n"); - } - - sb.append("CREATE TABLE IF NOT EXISTS "); - sb.append(metadata.toString()).append(" ("); - - List<ColumnMetadata> partitionKeyColumns = metadata.partitionKeyColumns(); - List<ColumnMetadata> clusteringColumns = getClusteringColumns(metadata); - List<ColumnMetadata> partitionColumns = getPartitionColumns(metadata); - - Consumer<StringBuilder> cdCommaAppender = commaAppender("\n\t"); - sb.append("\n\t"); - for (ColumnMetadata cfd: partitionKeyColumns) - { - cdCommaAppender.accept(sb); - sb.append(toCQL(cfd)); - if (partitionKeyColumns.size() == 1 && clusteringColumns.size() == 0) - sb.append(" PRIMARY KEY"); - } - - for (ColumnMetadata cfd: clusteringColumns) - { - cdCommaAppender.accept(sb); - sb.append(toCQL(cfd)); - } - - for (ColumnMetadata cfd: partitionColumns) - { - cdCommaAppender.accept(sb); - sb.append(toCQL(cfd, metadata.isStaticCompactTable())); - } - - if (includeDroppedColumns) - { - for (Map.Entry<ByteBuffer, DroppedColumn> entry: metadata.droppedColumns.entrySet()) - { - if (metadata.getColumn(entry.getKey()) != null) - continue; - - DroppedColumn droppedColumn = entry.getValue(); - cdCommaAppender.accept(sb); - sb.append(droppedColumn.column.name.toCQLString()); - sb.append(' '); - sb.append(droppedColumn.column.type.asCQL3Type().toString()); - } - } - - if (clusteringColumns.size() > 0 || partitionKeyColumns.size() > 1) - { - sb.append(",\n\tPRIMARY KEY ("); - if (partitionKeyColumns.size() > 1) - { - sb.append("("); - Consumer<StringBuilder> pkCommaAppender = commaAppender(" "); - for (ColumnMetadata cfd : partitionKeyColumns) - { - pkCommaAppender.accept(sb); - sb.append(cfd.name.toCQLString()); - } - sb.append(")"); - } - else - { - sb.append(partitionKeyColumns.get(0).name.toCQLString()); - } - - for (ColumnMetadata cfd : metadata.clusteringColumns()) - sb.append(", ").append(cfd.name.toCQLString()); - - sb.append(')'); - } - sb.append(")\n\t"); - sb.append("WITH "); - - sb.append("ID = ").append(metadata.id).append("\n\tAND "); - - if (metadata.isCompactTable()) - sb.append("COMPACT STORAGE\n\tAND "); - - if (clusteringColumns.size() > 0) - { - sb.append("CLUSTERING ORDER BY ("); - - Consumer<StringBuilder> cOrderCommaAppender = commaAppender(" "); - for (ColumnMetadata cd : clusteringColumns) - { - cOrderCommaAppender.accept(sb); - sb.append(cd.name.toCQLString()).append(' ').append(cd.clusteringOrder().toString()); - } - sb.append(")\n\tAND "); - } - - sb.append(toCQL(metadata.params)); - sb.append(";"); - - if (!isCqlCompatible(metadata)) - { - sb.append("\n*/"); - } - return sb.toString(); - } - - /** - * Build a CQL String representation of User Types used in the given Column Family. - * - * Type order is ensured as types are built incrementally: from the innermost (most nested) - * to the outermost. - */ - @VisibleForTesting - public static List<String> getUserTypesAsCQL(TableMetadata metadata) - { - List<AbstractType> types = new ArrayList<>(); - Set<AbstractType> typeSet = new HashSet<>(); - for (ColumnMetadata cd: Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns(), metadata.regularAndStaticColumns())) - { - AbstractType type = cd.type; - if (type.isUDT()) - resolveUserType((UserType) type, typeSet, types); - } - - List<String> typeStrings = new ArrayList<>(types.size()); - for (AbstractType type: types) - typeStrings.add(toCQL((UserType) type)); - return typeStrings; - } - - /** - * Build a CQL String representation of Dropped Columns in the given Column Family. - * - * If the column was dropped once, but is now re-created `ADD` will be appended accordingly. - */ - @VisibleForTesting - public static List<String> getDroppedColumnsAsCQL(TableMetadata metadata) - { - List<String> droppedColumns = new ArrayList<>(); - - for (Map.Entry<ByteBuffer, DroppedColumn> entry: metadata.droppedColumns.entrySet()) - { - DroppedColumn column = entry.getValue(); - droppedColumns.add(toCQLDrop(metadata, column)); - if (metadata.getColumn(entry.getKey()) != null) - droppedColumns.add(toCQLAdd(metadata, metadata.getColumn(entry.getKey()))); - } - - return droppedColumns; - } - - /** - * Build a CQL String representation of Indexes on columns in the given Column Family - */ - @VisibleForTesting - public static List<String> getIndexesAsCQL(TableMetadata metadata) - { - List<String> indexes = new ArrayList<>(metadata.indexes.size()); - for (IndexMetadata indexMetadata: metadata.indexes) - indexes.add(toCQL(metadata, indexMetadata)); - return indexes; - } - - private static String toCQL(TableMetadata baseTable, IndexMetadata indexMetadata) - { - if (indexMetadata.isCustom()) - { - Map<String, String> options = new HashMap<>(); - indexMetadata.options.forEach((k, v) -> { - if (!k.equals(IndexTarget.TARGET_OPTION_NAME) && !k.equals(IndexTarget.CUSTOM_INDEX_OPTION_NAME)) - options.put(k, v); - }); - - return String.format("CREATE CUSTOM INDEX %s ON %s (%s) USING '%s'%s;", - indexMetadata.toCQLString(), - baseTable.toString(), - indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME), - indexMetadata.options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME), - options.isEmpty() ? "" : " WITH OPTIONS " + toCQL(options)); - } - else - { - return String.format("CREATE INDEX %s ON %s (%s);", - indexMetadata.toCQLString(), - baseTable.toString(), - indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME)); - } - } - private static String toCQL(UserType userType) - { - StringBuilder sb = new StringBuilder(); - sb.append("CREATE TYPE ").append(userType.toCQLString()).append(" ("); - - Consumer<StringBuilder> commaAppender = commaAppender(" "); - for (int i = 0; i < userType.size(); i++) - { - commaAppender.accept(sb); - sb.append(String.format("%s %s", - userType.fieldNameAsString(i), - userType.fieldType(i).asCQL3Type())); - } - sb.append(");"); - return sb.toString(); - } - - private static String toCQL(TableParams tableParams) - { - StringBuilder builder = new StringBuilder(); - - builder.append("bloom_filter_fp_chance = ").append(tableParams.bloomFilterFpChance); - builder.append("\n\tAND dclocal_read_repair_chance = ").append(tableParams.dcLocalReadRepairChance); - builder.append("\n\tAND crc_check_chance = ").append(tableParams.crcCheckChance); - builder.append("\n\tAND default_time_to_live = ").append(tableParams.defaultTimeToLive); - builder.append("\n\tAND gc_grace_seconds = ").append(tableParams.gcGraceSeconds); - builder.append("\n\tAND min_index_interval = ").append(tableParams.minIndexInterval); - builder.append("\n\tAND max_index_interval = ").append(tableParams.maxIndexInterval); - builder.append("\n\tAND memtable_flush_period_in_ms = ").append(tableParams.memtableFlushPeriodInMs); - builder.append("\n\tAND read_repair_chance = ").append(tableParams.readRepairChance); - builder.append("\n\tAND speculative_retry = '").append(tableParams.speculativeRetry).append("'"); - builder.append("\n\tAND comment = ").append(singleQuote(tableParams.comment)); - builder.append("\n\tAND caching = ").append(toCQL(tableParams.caching.asMap())); - builder.append("\n\tAND compaction = ").append(toCQL(tableParams.compaction.asMap())); - builder.append("\n\tAND compression = ").append(toCQL(tableParams.compression.asMap())); - builder.append("\n\tAND cdc = ").append(tableParams.cdc); - - builder.append("\n\tAND extensions = { "); - for (Map.Entry<String, ByteBuffer> entry : tableParams.extensions.entrySet()) - { - builder.append(singleQuote(entry.getKey())); - builder.append(": "); - builder.append("0x").append(ByteBufferUtil.bytesToHex(entry.getValue())); - } - builder.append(" }"); - return builder.toString(); - } - - private static String toCQL(Map<?, ?> map) - { - StringBuilder builder = new StringBuilder("{ "); - - boolean isFirst = true; - for (Map.Entry entry: map.entrySet()) - { - if (isFirst) - isFirst = false; - else - builder.append(", "); - builder.append(singleQuote(entry.getKey().toString())); - builder.append(": "); - builder.append(singleQuote(entry.getValue().toString())); - } - - builder.append(" }"); - return builder.toString(); - } - - private static String toCQL(ColumnMetadata cd) - { - return toCQL(cd, false); - } - - private static String toCQL(ColumnMetadata cd, boolean isStaticCompactTable) - { - return String.format("%s %s%s", - cd.name.toCQLString(), - cd.type.asCQL3Type().toString(), - cd.isStatic() && !isStaticCompactTable ? " static" : ""); - } - - private static String toCQLAdd(TableMetadata table, ColumnMetadata cd) - { - return String.format("ALTER TABLE %s ADD %s %s%s;", - table.toString(), - cd.name.toCQLString(), - cd.type.asCQL3Type().toString(), - cd.isStatic() ? " static" : ""); - } - - private static String toCQLDrop(TableMetadata table, DroppedColumn droppedColumn) - { - return String.format("ALTER TABLE %s DROP %s USING TIMESTAMP %s;", - table.toString(), - droppedColumn.column.name.toCQLString(), - droppedColumn.droppedTime); - } - - private static void resolveUserType(UserType type, Set<AbstractType> typeSet, List<AbstractType> types) - { - for (AbstractType subType: type.fieldTypes()) - if (!typeSet.contains(subType) && subType.isUDT()) - resolveUserType((UserType) subType, typeSet, types); - - if (!typeSet.contains(type)) - { - typeSet.add(type); - types.add(type); - } - } - - private static String singleQuote(String s) - { - return String.format("'%s'", s.replaceAll("'", "''")); - } - - private static Consumer<StringBuilder> commaAppender(String afterComma) - { - AtomicBoolean isFirst = new AtomicBoolean(true); - return new Consumer<StringBuilder>() - { - public void accept(StringBuilder stringBuilder) - { - if (!isFirst.getAndSet(false)) - stringBuilder.append(',').append(afterComma); - } - }; - } - - /** - * Whether or not the given metadata is compatible / representable with CQL Language - */ - public static boolean isCqlCompatible(TableMetadata metaData) - { - if (metaData.isSuper()) - return false; - - if (metaData.isCompactTable() - && metaData.regularColumns().size() > 1 - && metaData.clusteringColumns().size() >= 1) - return false; - - return true; - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/src/java/org/apache/cassandra/db/TableCQLHelper.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/TableCQLHelper.java b/src/java/org/apache/cassandra/db/TableCQLHelper.java new file mode 100644 index 0000000..862cf30 --- /dev/null +++ b/src/java/org/apache/cassandra/db/TableCQLHelper.java @@ -0,0 +1,428 @@ +/* + * 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.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Iterables; + +import org.apache.cassandra.cql3.statements.*; +import org.apache.cassandra.db.marshal.*; +import org.apache.cassandra.schema.*; +import org.apache.cassandra.utils.*; + +/** + * Helper methods to represent TableMetadata and related objects in CQL format + */ +public class TableCQLHelper +{ + public static List<String> dumpReCreateStatements(TableMetadata metadata) + { + List<String> l = new ArrayList<>(); + // Types come first, as table can't be created without them + l.addAll(TableCQLHelper.getUserTypesAsCQL(metadata)); + // Record re-create schema statements + l.add(TableCQLHelper.getTableMetadataAsCQL(metadata, true)); + // Dropped columns (and re-additions) + l.addAll(TableCQLHelper.getDroppedColumnsAsCQL(metadata)); + // Indexes applied as last, since otherwise they may interfere with column drops / re-additions + l.addAll(TableCQLHelper.getIndexesAsCQL(metadata)); + return l; + } + + private static List<ColumnMetadata> getClusteringColumns(TableMetadata metadata) + { + List<ColumnMetadata> cds = new ArrayList<>(metadata.clusteringColumns().size()); + + if (!metadata.isStaticCompactTable()) + for (ColumnMetadata cd : metadata.clusteringColumns()) + cds.add(cd); + + return cds; + } + + private static List<ColumnMetadata> getPartitionColumns(TableMetadata metadata) + { + List<ColumnMetadata> cds = new ArrayList<>(metadata.regularAndStaticColumns().size()); + + for (ColumnMetadata cd : metadata.staticColumns()) + cds.add(cd); + + if (metadata.isDense()) + { + // remove an empty type + for (ColumnMetadata cd : metadata.regularColumns()) + if (!cd.type.equals(EmptyType.instance)) + cds.add(cd); + } + // "regular" columns are not exposed for static compact tables + else if (!metadata.isStaticCompactTable()) + { + for (ColumnMetadata cd : metadata.regularColumns()) + cds.add(cd); + } + + return cds; + } + + /** + * Build a CQL String representation of Table Metadata + */ + @VisibleForTesting + public static String getTableMetadataAsCQL(TableMetadata metadata, boolean includeDroppedColumns) + { + StringBuilder sb = new StringBuilder(); + if (!isCqlCompatible(metadata)) + { + sb.append(String.format("/*\nWarning: Table %s omitted because it has constructs not compatible with CQL (was created via legacy API).\n", + metadata.toString())); + sb.append("\nApproximate structure, for reference:"); + sb.append("\n(this should not be used to reproduce this schema)\n\n"); + } + + sb.append("CREATE TABLE IF NOT EXISTS "); + sb.append(metadata.toString()).append(" ("); + + List<ColumnMetadata> partitionKeyColumns = metadata.partitionKeyColumns(); + List<ColumnMetadata> clusteringColumns = getClusteringColumns(metadata); + List<ColumnMetadata> partitionColumns = getPartitionColumns(metadata); + + Consumer<StringBuilder> cdCommaAppender = commaAppender("\n\t"); + sb.append("\n\t"); + for (ColumnMetadata cfd: partitionKeyColumns) + { + cdCommaAppender.accept(sb); + sb.append(toCQL(cfd)); + if (partitionKeyColumns.size() == 1 && clusteringColumns.size() == 0) + sb.append(" PRIMARY KEY"); + } + + for (ColumnMetadata cfd: clusteringColumns) + { + cdCommaAppender.accept(sb); + sb.append(toCQL(cfd)); + } + + for (ColumnMetadata cfd: partitionColumns) + { + cdCommaAppender.accept(sb); + sb.append(toCQL(cfd, metadata.isStaticCompactTable())); + } + + if (includeDroppedColumns) + { + for (Map.Entry<ByteBuffer, DroppedColumn> entry: metadata.droppedColumns.entrySet()) + { + if (metadata.getColumn(entry.getKey()) != null) + continue; + + DroppedColumn droppedColumn = entry.getValue(); + cdCommaAppender.accept(sb); + sb.append(droppedColumn.column.name.toCQLString()); + sb.append(' '); + sb.append(droppedColumn.column.type.asCQL3Type().toString()); + } + } + + if (clusteringColumns.size() > 0 || partitionKeyColumns.size() > 1) + { + sb.append(",\n\tPRIMARY KEY ("); + if (partitionKeyColumns.size() > 1) + { + sb.append("("); + Consumer<StringBuilder> pkCommaAppender = commaAppender(" "); + for (ColumnMetadata cfd : partitionKeyColumns) + { + pkCommaAppender.accept(sb); + sb.append(cfd.name.toCQLString()); + } + sb.append(")"); + } + else + { + sb.append(partitionKeyColumns.get(0).name.toCQLString()); + } + + for (ColumnMetadata cfd : metadata.clusteringColumns()) + sb.append(", ").append(cfd.name.toCQLString()); + + sb.append(')'); + } + sb.append(")\n\t"); + sb.append("WITH "); + + sb.append("ID = ").append(metadata.id).append("\n\tAND "); + + if (metadata.isCompactTable()) + sb.append("COMPACT STORAGE\n\tAND "); + + if (clusteringColumns.size() > 0) + { + sb.append("CLUSTERING ORDER BY ("); + + Consumer<StringBuilder> cOrderCommaAppender = commaAppender(" "); + for (ColumnMetadata cd : clusteringColumns) + { + cOrderCommaAppender.accept(sb); + sb.append(cd.name.toCQLString()).append(' ').append(cd.clusteringOrder().toString()); + } + sb.append(")\n\tAND "); + } + + sb.append(toCQL(metadata.params)); + sb.append(";"); + + if (!isCqlCompatible(metadata)) + { + sb.append("\n*/"); + } + return sb.toString(); + } + + /** + * Build a CQL String representation of User Types used in the given Table. + * + * Type order is ensured as types are built incrementally: from the innermost (most nested) + * to the outermost. + */ + @VisibleForTesting + public static List<String> getUserTypesAsCQL(TableMetadata metadata) + { + List<AbstractType> types = new ArrayList<>(); + Set<AbstractType> typeSet = new HashSet<>(); + for (ColumnMetadata cd: Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns(), metadata.regularAndStaticColumns())) + { + AbstractType type = cd.type; + if (type.isUDT()) + resolveUserType((UserType) type, typeSet, types); + } + + List<String> typeStrings = new ArrayList<>(types.size()); + for (AbstractType type: types) + typeStrings.add(toCQL((UserType) type)); + return typeStrings; + } + + /** + * Build a CQL String representation of Dropped Columns in the given Table. + * + * If the column was dropped once, but is now re-created `ADD` will be appended accordingly. + */ + @VisibleForTesting + public static List<String> getDroppedColumnsAsCQL(TableMetadata metadata) + { + List<String> droppedColumns = new ArrayList<>(); + + for (Map.Entry<ByteBuffer, DroppedColumn> entry: metadata.droppedColumns.entrySet()) + { + DroppedColumn column = entry.getValue(); + droppedColumns.add(toCQLDrop(metadata, column)); + if (metadata.getColumn(entry.getKey()) != null) + droppedColumns.add(toCQLAdd(metadata, metadata.getColumn(entry.getKey()))); + } + + return droppedColumns; + } + + /** + * Build a CQL String representation of Indexes on columns in the given Table + */ + @VisibleForTesting + public static List<String> getIndexesAsCQL(TableMetadata metadata) + { + List<String> indexes = new ArrayList<>(metadata.indexes.size()); + for (IndexMetadata indexMetadata: metadata.indexes) + indexes.add(toCQL(metadata, indexMetadata)); + return indexes; + } + + private static String toCQL(TableMetadata baseTable, IndexMetadata indexMetadata) + { + if (indexMetadata.isCustom()) + { + Map<String, String> options = new HashMap<>(); + indexMetadata.options.forEach((k, v) -> { + if (!k.equals(IndexTarget.TARGET_OPTION_NAME) && !k.equals(IndexTarget.CUSTOM_INDEX_OPTION_NAME)) + options.put(k, v); + }); + + return String.format("CREATE CUSTOM INDEX %s ON %s (%s) USING '%s'%s;", + indexMetadata.toCQLString(), + baseTable.toString(), + indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME), + indexMetadata.options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME), + options.isEmpty() ? "" : " WITH OPTIONS " + toCQL(options)); + } + else + { + return String.format("CREATE INDEX %s ON %s (%s);", + indexMetadata.toCQLString(), + baseTable.toString(), + indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME)); + } + } + private static String toCQL(UserType userType) + { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TYPE ").append(userType.toCQLString()).append(" ("); + + Consumer<StringBuilder> commaAppender = commaAppender(" "); + for (int i = 0; i < userType.size(); i++) + { + commaAppender.accept(sb); + sb.append(String.format("%s %s", + userType.fieldNameAsString(i), + userType.fieldType(i).asCQL3Type())); + } + sb.append(");"); + return sb.toString(); + } + + private static String toCQL(TableParams tableParams) + { + StringBuilder builder = new StringBuilder(); + + builder.append("bloom_filter_fp_chance = ").append(tableParams.bloomFilterFpChance); + builder.append("\n\tAND dclocal_read_repair_chance = ").append(tableParams.dcLocalReadRepairChance); + builder.append("\n\tAND crc_check_chance = ").append(tableParams.crcCheckChance); + builder.append("\n\tAND default_time_to_live = ").append(tableParams.defaultTimeToLive); + builder.append("\n\tAND gc_grace_seconds = ").append(tableParams.gcGraceSeconds); + builder.append("\n\tAND min_index_interval = ").append(tableParams.minIndexInterval); + builder.append("\n\tAND max_index_interval = ").append(tableParams.maxIndexInterval); + builder.append("\n\tAND memtable_flush_period_in_ms = ").append(tableParams.memtableFlushPeriodInMs); + builder.append("\n\tAND read_repair_chance = ").append(tableParams.readRepairChance); + builder.append("\n\tAND speculative_retry = '").append(tableParams.speculativeRetry).append("'"); + builder.append("\n\tAND comment = ").append(singleQuote(tableParams.comment)); + builder.append("\n\tAND caching = ").append(toCQL(tableParams.caching.asMap())); + builder.append("\n\tAND compaction = ").append(toCQL(tableParams.compaction.asMap())); + builder.append("\n\tAND compression = ").append(toCQL(tableParams.compression.asMap())); + builder.append("\n\tAND cdc = ").append(tableParams.cdc); + + builder.append("\n\tAND extensions = { "); + for (Map.Entry<String, ByteBuffer> entry : tableParams.extensions.entrySet()) + { + builder.append(singleQuote(entry.getKey())); + builder.append(": "); + builder.append("0x").append(ByteBufferUtil.bytesToHex(entry.getValue())); + } + builder.append(" }"); + return builder.toString(); + } + + private static String toCQL(Map<?, ?> map) + { + StringBuilder builder = new StringBuilder("{ "); + + boolean isFirst = true; + for (Map.Entry entry: map.entrySet()) + { + if (isFirst) + isFirst = false; + else + builder.append(", "); + builder.append(singleQuote(entry.getKey().toString())); + builder.append(": "); + builder.append(singleQuote(entry.getValue().toString())); + } + + builder.append(" }"); + return builder.toString(); + } + + private static String toCQL(ColumnMetadata cd) + { + return toCQL(cd, false); + } + + private static String toCQL(ColumnMetadata cd, boolean isStaticCompactTable) + { + return String.format("%s %s%s", + cd.name.toCQLString(), + cd.type.asCQL3Type().toString(), + cd.isStatic() && !isStaticCompactTable ? " static" : ""); + } + + private static String toCQLAdd(TableMetadata table, ColumnMetadata cd) + { + return String.format("ALTER TABLE %s ADD %s %s%s;", + table.toString(), + cd.name.toCQLString(), + cd.type.asCQL3Type().toString(), + cd.isStatic() ? " static" : ""); + } + + private static String toCQLDrop(TableMetadata table, DroppedColumn droppedColumn) + { + return String.format("ALTER TABLE %s DROP %s USING TIMESTAMP %s;", + table.toString(), + droppedColumn.column.name.toCQLString(), + droppedColumn.droppedTime); + } + + private static void resolveUserType(UserType type, Set<AbstractType> typeSet, List<AbstractType> types) + { + for (AbstractType subType: type.fieldTypes()) + if (!typeSet.contains(subType) && subType.isUDT()) + resolveUserType((UserType) subType, typeSet, types); + + if (!typeSet.contains(type)) + { + typeSet.add(type); + types.add(type); + } + } + + private static String singleQuote(String s) + { + return String.format("'%s'", s.replaceAll("'", "''")); + } + + private static Consumer<StringBuilder> commaAppender(String afterComma) + { + AtomicBoolean isFirst = new AtomicBoolean(true); + return new Consumer<StringBuilder>() + { + public void accept(StringBuilder stringBuilder) + { + if (!isFirst.getAndSet(false)) + stringBuilder.append(',').append(afterComma); + } + }; + } + + /** + * Whether or not the given metadata is compatible / representable with CQL Language + */ + public static boolean isCqlCompatible(TableMetadata metaData) + { + if (metaData.isSuper()) + return false; + + if (metaData.isCompactTable() + && metaData.regularColumns().size() > 1 + && metaData.clusteringColumns().size() >= 1) + return false; + + return true; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java deleted file mode 100644 index 4fcaba7..0000000 --- a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * 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.io.FileReader; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.*; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.Files; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import org.apache.cassandra.*; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.cql3.statements.*; -import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.index.sasi.*; -import org.apache.cassandra.schema.*; -import org.apache.cassandra.service.reads.AlwaysSpeculativeRetryPolicy; -import org.apache.cassandra.utils.*; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class ColumnFamilyStoreCQLHelperTest extends CQLTester -{ - @Before - public void defineSchema() throws ConfigurationException - { - SchemaLoader.prepareServer(); - } - - @Test - public void testUserTypesCQL() - { - String keyspace = "cql_test_keyspace_user_types"; - String table = "test_table_user_types"; - - UserType typeA = new UserType(keyspace, ByteBufferUtil.bytes("a"), - Arrays.asList(FieldIdentifier.forUnquoted("a1"), - FieldIdentifier.forUnquoted("a2"), - FieldIdentifier.forUnquoted("a3")), - Arrays.asList(IntegerType.instance, - IntegerType.instance, - IntegerType.instance), - true); - - UserType typeB = new UserType(keyspace, ByteBufferUtil.bytes("b"), - Arrays.asList(FieldIdentifier.forUnquoted("b1"), - FieldIdentifier.forUnquoted("b2"), - FieldIdentifier.forUnquoted("b3")), - Arrays.asList(typeA, - typeA, - typeA), - true); - - UserType typeC = new UserType(keyspace, ByteBufferUtil.bytes("c"), - Arrays.asList(FieldIdentifier.forUnquoted("c1"), - FieldIdentifier.forUnquoted("c2"), - FieldIdentifier.forUnquoted("c3")), - Arrays.asList(typeB, - typeB, - typeB), - true); - - TableMetadata cfm = - TableMetadata.builder(keyspace, table) - .addPartitionKeyColumn("pk1", IntegerType.instance) - .addClusteringColumn("ck1", IntegerType.instance) - .addRegularColumn("reg1", typeC) - .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false)) - .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true)) - .build(); - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), Tables.of(cfm), Types.of(typeA, typeB, typeC)); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - assertEquals(ImmutableList.of("CREATE TYPE cql_test_keyspace_user_types.a (a1 varint, a2 varint, a3 varint);", - "CREATE TYPE cql_test_keyspace_user_types.b (b1 a, b2 a, b3 a);", - "CREATE TYPE cql_test_keyspace_user_types.c (c1 b, c2 b, c3 b);"), - ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(cfs.metadata())); - } - - @Test - public void testDroppedColumnsCQL() - { - String keyspace = "cql_test_keyspace_dropped_columns"; - String table = "test_table_dropped_columns"; - - TableMetadata.Builder builder = - TableMetadata.builder(keyspace, table) - .addPartitionKeyColumn("pk1", IntegerType.instance) - .addClusteringColumn("ck1", IntegerType.instance) - .addRegularColumn("reg1", IntegerType.instance) - .addRegularColumn("reg2", IntegerType.instance) - .addRegularColumn("reg3", IntegerType.instance); - - ColumnMetadata reg1 = builder.getColumn(ByteBufferUtil.bytes("reg1")); - ColumnMetadata reg2 = builder.getColumn(ByteBufferUtil.bytes("reg2")); - ColumnMetadata reg3 = builder.getColumn(ByteBufferUtil.bytes("reg3")); - - builder.removeRegularOrStaticColumn(reg1.name) - .removeRegularOrStaticColumn(reg2.name) - .removeRegularOrStaticColumn(reg3.name); - - builder.recordColumnDrop(reg1, 10000) - .recordColumnDrop(reg2, 20000) - .recordColumnDrop(reg3, 30000); - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg1 USING TIMESTAMP 10000;", - "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg3 USING TIMESTAMP 30000;", - "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg2 USING TIMESTAMP 20000;"), - ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata())); - - assertTrue(ColumnFamilyStoreCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( - "CREATE TABLE IF NOT EXISTS cql_test_keyspace_dropped_columns.test_table_dropped_columns (\n" + - "\tpk1 varint,\n" + - "\tck1 varint,\n" + - "\treg1 varint,\n" + - "\treg3 varint,\n" + - "\treg2 varint,\n" + - "\tPRIMARY KEY (pk1, ck1))")); - } - - @Test - public void testReaddedColumns() - { - String keyspace = "cql_test_keyspace_readded_columns"; - String table = "test_table_readded_columns"; - - TableMetadata.Builder builder = - TableMetadata.builder(keyspace, table) - .addPartitionKeyColumn("pk1", IntegerType.instance) - .addClusteringColumn("ck1", IntegerType.instance) - .addRegularColumn("reg1", IntegerType.instance) - .addStaticColumn("reg2", IntegerType.instance) - .addRegularColumn("reg3", IntegerType.instance); - - ColumnMetadata reg1 = builder.getColumn(ByteBufferUtil.bytes("reg1")); - ColumnMetadata reg2 = builder.getColumn(ByteBufferUtil.bytes("reg2")); - - builder.removeRegularOrStaticColumn(reg1.name); - builder.removeRegularOrStaticColumn(reg2.name); - - builder.recordColumnDrop(reg1, 10000); - builder.recordColumnDrop(reg2, 20000); - - builder.addColumn(reg1); - builder.addColumn(reg2); - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - // when re-adding, column is present in CREATE, then in DROP and then in ADD again, to record DROP with a proper timestamp - assertTrue(ColumnFamilyStoreCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( - "CREATE TABLE IF NOT EXISTS cql_test_keyspace_readded_columns.test_table_readded_columns (\n" + - "\tpk1 varint,\n" + - "\tck1 varint,\n" + - "\treg2 varint static,\n" + - "\treg1 varint,\n" + - "\treg3 varint,\n" + - "\tPRIMARY KEY (pk1, ck1))")); - - assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg1 USING TIMESTAMP 10000;", - "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg1 varint;", - "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg2 USING TIMESTAMP 20000;", - "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg2 varint static;"), - ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata())); - } - - @Test - public void testCfmColumnsCQL() - { - String keyspace = "cql_test_keyspace_create_table"; - String table = "test_table_create_table"; - - TableMetadata.Builder metadata = - TableMetadata.builder(keyspace, table) - .addPartitionKeyColumn("pk1", IntegerType.instance) - .addPartitionKeyColumn("pk2", AsciiType.instance) - .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance)) - .addClusteringColumn("ck2", IntegerType.instance) - .addStaticColumn("st1", AsciiType.instance) - .addRegularColumn("reg1", AsciiType.instance) - .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false)) - .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true)); - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), metadata); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - assertTrue(ColumnFamilyStoreCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( - "CREATE TABLE IF NOT EXISTS cql_test_keyspace_create_table.test_table_create_table (\n" + - "\tpk1 varint,\n" + - "\tpk2 ascii,\n" + - "\tck1 varint,\n" + - "\tck2 varint,\n" + - "\tst1 ascii static,\n" + - "\treg1 ascii,\n" + - "\treg2 frozen<list<varint>>,\n" + - "\treg3 map<ascii, varint>,\n" + - "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" + - "\tWITH ID = " + cfs.metadata.id + "\n" + - "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)")); - } - - @Test - public void testCfmOptionsCQL() - { - String keyspace = "cql_test_keyspace_options"; - String table = "test_table_options"; - - TableMetadata.Builder builder = TableMetadata.builder(keyspace, table); - builder.addPartitionKeyColumn("pk1", IntegerType.instance) - .addClusteringColumn("cl1", IntegerType.instance) - .addRegularColumn("reg1", AsciiType.instance) - .bloomFilterFpChance(1.0) - .comment("comment") - .compaction(CompactionParams.lcs(Collections.singletonMap("sstable_size_in_mb", "1"))) - .compression(CompressionParams.lz4(1 << 16, 1 << 15)) - .dcLocalReadRepairChance(0.2) - .crcCheckChance(0.3) - .defaultTimeToLive(4) - .gcGraceSeconds(5) - .minIndexInterval(6) - .maxIndexInterval(7) - .memtableFlushPeriod(8) - .readRepairChance(0.9) - .speculativeRetry(AlwaysSpeculativeRetryPolicy.INSTANCE) - .extensions(ImmutableMap.of("ext1", ByteBuffer.wrap("val1".getBytes()))) - .recordColumnDrop(ColumnMetadata.regularColumn(keyspace, table, "reg1", AsciiType.instance), - FBUtilities.timestampMicros()); - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - assertTrue(ColumnFamilyStoreCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).endsWith( - "AND bloom_filter_fp_chance = 1.0\n" + - "\tAND dclocal_read_repair_chance = 0.2\n" + - "\tAND crc_check_chance = 0.3\n" + - "\tAND default_time_to_live = 4\n" + - "\tAND gc_grace_seconds = 5\n" + - "\tAND min_index_interval = 6\n" + - "\tAND max_index_interval = 7\n" + - "\tAND memtable_flush_period_in_ms = 8\n" + - "\tAND read_repair_chance = 0.9\n" + - "\tAND speculative_retry = 'ALWAYS'\n" + - "\tAND comment = 'comment'\n" + - "\tAND caching = { 'keys': 'ALL', 'rows_per_partition': 'NONE' }\n" + - "\tAND compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'sstable_size_in_mb': '1' }\n" + - "\tAND compression = { 'chunk_length_in_kb': '64', 'min_compress_ratio': '2.0', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor' }\n" + - "\tAND cdc = false\n" + - "\tAND extensions = { 'ext1': 0x76616c31 };" - )); - } - - @Test - public void testCfmIndexJson() - { - String keyspace = "cql_test_keyspace_3"; - String table = "test_table_3"; - - TableMetadata.Builder builder = - TableMetadata.builder(keyspace, table) - .addPartitionKeyColumn("pk1", IntegerType.instance) - .addClusteringColumn("cl1", IntegerType.instance) - .addRegularColumn("reg1", AsciiType.instance); - - ColumnIdentifier reg1 = ColumnIdentifier.getInterned("reg1", true); - - builder.indexes( - Indexes.of(IndexMetadata.fromIndexTargets( - Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.VALUES)), - "indexName", - IndexMetadata.Kind.COMPOSITES, - Collections.emptyMap()), - IndexMetadata.fromIndexTargets( - Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS)), - "indexName2", - IndexMetadata.Kind.COMPOSITES, - Collections.emptyMap()), - IndexMetadata.fromIndexTargets( - Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS_AND_VALUES)), - "indexName3", - IndexMetadata.Kind.COMPOSITES, - Collections.emptyMap()), - IndexMetadata.fromIndexTargets( - Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS_AND_VALUES)), - "indexName4", - IndexMetadata.Kind.CUSTOM, - Collections.singletonMap(IndexTarget.CUSTOM_INDEX_OPTION_NAME, SASIIndex.class.getName())))); - - - SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); - - ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); - - assertEquals(ImmutableList.of("CREATE INDEX \"indexName\" ON cql_test_keyspace_3.test_table_3 (values(reg1));", - "CREATE INDEX \"indexName2\" ON cql_test_keyspace_3.test_table_3 (keys(reg1));", - "CREATE INDEX \"indexName3\" ON cql_test_keyspace_3.test_table_3 (entries(reg1));", - "CREATE CUSTOM INDEX \"indexName4\" ON cql_test_keyspace_3.test_table_3 (entries(reg1)) USING 'org.apache.cassandra.index.sasi.SASIIndex';"), - ColumnFamilyStoreCQLHelper.getIndexesAsCQL(cfs.metadata())); - } - - private final static String SNAPSHOT = "testsnapshot"; - - @Test - public void testSnapshot() throws Throwable - { - String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);"); - String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);"); - String typeC = createType("CREATE TYPE %s (c1 frozen<" + typeB + ">, c2 frozen<" + typeB + ">, c3 frozen<" + typeB + ">);"); - - String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" + - "pk1 varint," + - "pk2 ascii," + - "ck1 varint," + - "ck2 varint," + - "reg1 " + typeC + "," + - "reg2 int," + - "reg3 int," + - "PRIMARY KEY ((pk1, pk2), ck1, ck2)) WITH " + - "CLUSTERING ORDER BY (ck1 ASC, ck2 DESC);"); - - alterTable("ALTER TABLE %s DROP reg3 USING TIMESTAMP 10000;"); - alterTable("ALTER TABLE %s ADD reg3 int;"); - - for (int i = 0; i < 10; i++) - execute("INSERT INTO %s (pk1, pk2, ck1, ck2, reg1, reg2) VALUES (?, ?, ?, ?, ?, ?)", i, i + 1, i + 2, i + 3, null, i + 5); - - ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName); - cfs.snapshot(SNAPSHOT); - - String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset()); - assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (a1 varint, a2 varint, a3 varint);", keyspace(), typeA))); - assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (a1 varint, a2 varint, a3 varint);", keyspace(), typeA))); - assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (b1 frozen<%s>, b2 frozen<%s>, b3 frozen<%s>);", keyspace(), typeB, typeA, typeA, typeA))); - assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (c1 frozen<%s>, c2 frozen<%s>, c3 frozen<%s>);", keyspace(), typeC, typeB, typeB, typeB))); - - schema = schema.substring(schema.indexOf("CREATE TABLE")); // trim to ensure order - - assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" + - "\tpk1 varint,\n" + - "\tpk2 ascii,\n" + - "\tck1 varint,\n" + - "\tck2 varint,\n" + - "\treg2 int,\n" + - "\treg3 int,\n" + - "\treg1 " + typeC + ",\n" + - "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" + - "\tWITH ID = " + cfs.metadata.id + "\n" + - "\tAND CLUSTERING ORDER BY (ck1 ASC, ck2 DESC)")); - - schema = schema.substring(schema.indexOf("ALTER")); - assertTrue(schema.startsWith(String.format("ALTER TABLE %s.%s DROP reg3 USING TIMESTAMP 10000;", keyspace(), tableName))); - assertTrue(schema.contains(String.format("ALTER TABLE %s.%s ADD reg3 int;", keyspace(), tableName))); - - JSONObject manifest = (JSONObject) new JSONParser().parse(new FileReader(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT))); - JSONArray files = (JSONArray) manifest.get("files"); - Assert.assertEquals(1, files.size()); - } - - @Test - public void testSystemKsSnapshot() throws Throwable - { - ColumnFamilyStore cfs = Keyspace.open("system").getColumnFamilyStore("peers"); - cfs.snapshot(SNAPSHOT); - - Assert.assertTrue(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT).exists()); - Assert.assertFalse(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT).exists()); - } - - @Test - public void testDroppedType() throws Throwable - { - String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);"); - String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);"); - - String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" + - "pk1 varint," + - "ck1 varint," + - "reg1 " + typeB + "," + - "reg2 varint," + - "PRIMARY KEY (pk1, ck1));"); - - alterTable("ALTER TABLE %s DROP reg1 USING TIMESTAMP 10000;"); - - Runnable validate = () -> { - try - { - ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName); - cfs.snapshot(SNAPSHOT); - String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset()); - - // When both column and it's type are dropped, the type in column definition gets substituted with a tuple - assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" + - "\tpk1 varint,\n" + - "\tck1 varint,\n" + - "\treg2 varint,\n" + - "\treg1 frozen<tuple<frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>>>,\n" + - "\tPRIMARY KEY (pk1, ck1))")); - assertTrue(schema.contains("ALTER TABLE " + keyspace() + "." + tableName + " DROP reg1 USING TIMESTAMP 10000;")); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - }; - - // Validate before and after the type drop - validate.run(); - schemaChange("DROP TYPE " + keyspace() + "." + typeB); - schemaChange("DROP TYPE " + keyspace() + "." + typeA); - validate.run(); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/e75c5171/test/unit/org/apache/cassandra/db/TableCQLHelperTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/db/TableCQLHelperTest.java b/test/unit/org/apache/cassandra/db/TableCQLHelperTest.java new file mode 100644 index 0000000..cfc9686 --- /dev/null +++ b/test/unit/org/apache/cassandra/db/TableCQLHelperTest.java @@ -0,0 +1,447 @@ +/* + * 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.io.FileReader; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.*; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.Files; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.apache.cassandra.*; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.statements.*; +import org.apache.cassandra.db.marshal.*; +import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.index.sasi.*; +import org.apache.cassandra.schema.*; +import org.apache.cassandra.service.reads.AlwaysSpeculativeRetryPolicy; +import org.apache.cassandra.utils.*; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TableCQLHelperTest extends CQLTester +{ + @Before + public void defineSchema() throws ConfigurationException + { + SchemaLoader.prepareServer(); + } + + @Test + public void testUserTypesCQL() + { + String keyspace = "cql_test_keyspace_user_types"; + String table = "test_table_user_types"; + + UserType typeA = new UserType(keyspace, ByteBufferUtil.bytes("a"), + Arrays.asList(FieldIdentifier.forUnquoted("a1"), + FieldIdentifier.forUnquoted("a2"), + FieldIdentifier.forUnquoted("a3")), + Arrays.asList(IntegerType.instance, + IntegerType.instance, + IntegerType.instance), + true); + + UserType typeB = new UserType(keyspace, ByteBufferUtil.bytes("b"), + Arrays.asList(FieldIdentifier.forUnquoted("b1"), + FieldIdentifier.forUnquoted("b2"), + FieldIdentifier.forUnquoted("b3")), + Arrays.asList(typeA, + typeA, + typeA), + true); + + UserType typeC = new UserType(keyspace, ByteBufferUtil.bytes("c"), + Arrays.asList(FieldIdentifier.forUnquoted("c1"), + FieldIdentifier.forUnquoted("c2"), + FieldIdentifier.forUnquoted("c3")), + Arrays.asList(typeB, + typeB, + typeB), + true); + + TableMetadata cfm = + TableMetadata.builder(keyspace, table) + .addPartitionKeyColumn("pk1", IntegerType.instance) + .addClusteringColumn("ck1", IntegerType.instance) + .addRegularColumn("reg1", typeC) + .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false)) + .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true)) + .build(); + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), Tables.of(cfm), Types.of(typeA, typeB, typeC)); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + assertEquals(ImmutableList.of("CREATE TYPE cql_test_keyspace_user_types.a (a1 varint, a2 varint, a3 varint);", + "CREATE TYPE cql_test_keyspace_user_types.b (b1 a, b2 a, b3 a);", + "CREATE TYPE cql_test_keyspace_user_types.c (c1 b, c2 b, c3 b);"), + TableCQLHelper.getUserTypesAsCQL(cfs.metadata())); + } + + @Test + public void testDroppedColumnsCQL() + { + String keyspace = "cql_test_keyspace_dropped_columns"; + String table = "test_table_dropped_columns"; + + TableMetadata.Builder builder = + TableMetadata.builder(keyspace, table) + .addPartitionKeyColumn("pk1", IntegerType.instance) + .addClusteringColumn("ck1", IntegerType.instance) + .addRegularColumn("reg1", IntegerType.instance) + .addRegularColumn("reg2", IntegerType.instance) + .addRegularColumn("reg3", IntegerType.instance); + + ColumnMetadata reg1 = builder.getColumn(ByteBufferUtil.bytes("reg1")); + ColumnMetadata reg2 = builder.getColumn(ByteBufferUtil.bytes("reg2")); + ColumnMetadata reg3 = builder.getColumn(ByteBufferUtil.bytes("reg3")); + + builder.removeRegularOrStaticColumn(reg1.name) + .removeRegularOrStaticColumn(reg2.name) + .removeRegularOrStaticColumn(reg3.name); + + builder.recordColumnDrop(reg1, 10000) + .recordColumnDrop(reg2, 20000) + .recordColumnDrop(reg3, 30000); + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg1 USING TIMESTAMP 10000;", + "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg3 USING TIMESTAMP 30000;", + "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg2 USING TIMESTAMP 20000;"), + TableCQLHelper.getDroppedColumnsAsCQL(cfs.metadata())); + + assertTrue(TableCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( + "CREATE TABLE IF NOT EXISTS cql_test_keyspace_dropped_columns.test_table_dropped_columns (\n" + + "\tpk1 varint,\n" + + "\tck1 varint,\n" + + "\treg1 varint,\n" + + "\treg3 varint,\n" + + "\treg2 varint,\n" + + "\tPRIMARY KEY (pk1, ck1))")); + } + + @Test + public void testReaddedColumns() + { + String keyspace = "cql_test_keyspace_readded_columns"; + String table = "test_table_readded_columns"; + + TableMetadata.Builder builder = + TableMetadata.builder(keyspace, table) + .addPartitionKeyColumn("pk1", IntegerType.instance) + .addClusteringColumn("ck1", IntegerType.instance) + .addRegularColumn("reg1", IntegerType.instance) + .addStaticColumn("reg2", IntegerType.instance) + .addRegularColumn("reg3", IntegerType.instance); + + ColumnMetadata reg1 = builder.getColumn(ByteBufferUtil.bytes("reg1")); + ColumnMetadata reg2 = builder.getColumn(ByteBufferUtil.bytes("reg2")); + + builder.removeRegularOrStaticColumn(reg1.name); + builder.removeRegularOrStaticColumn(reg2.name); + + builder.recordColumnDrop(reg1, 10000); + builder.recordColumnDrop(reg2, 20000); + + builder.addColumn(reg1); + builder.addColumn(reg2); + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + // when re-adding, column is present in CREATE, then in DROP and then in ADD again, to record DROP with a proper timestamp + assertTrue(TableCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( + "CREATE TABLE IF NOT EXISTS cql_test_keyspace_readded_columns.test_table_readded_columns (\n" + + "\tpk1 varint,\n" + + "\tck1 varint,\n" + + "\treg2 varint static,\n" + + "\treg1 varint,\n" + + "\treg3 varint,\n" + + "\tPRIMARY KEY (pk1, ck1))")); + + assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg1 USING TIMESTAMP 10000;", + "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg1 varint;", + "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg2 USING TIMESTAMP 20000;", + "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg2 varint static;"), + TableCQLHelper.getDroppedColumnsAsCQL(cfs.metadata())); + } + + @Test + public void testCfmColumnsCQL() + { + String keyspace = "cql_test_keyspace_create_table"; + String table = "test_table_create_table"; + + TableMetadata.Builder metadata = + TableMetadata.builder(keyspace, table) + .addPartitionKeyColumn("pk1", IntegerType.instance) + .addPartitionKeyColumn("pk2", AsciiType.instance) + .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance)) + .addClusteringColumn("ck2", IntegerType.instance) + .addStaticColumn("st1", AsciiType.instance) + .addRegularColumn("reg1", AsciiType.instance) + .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false)) + .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true)); + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), metadata); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + assertTrue(TableCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).startsWith( + "CREATE TABLE IF NOT EXISTS cql_test_keyspace_create_table.test_table_create_table (\n" + + "\tpk1 varint,\n" + + "\tpk2 ascii,\n" + + "\tck1 varint,\n" + + "\tck2 varint,\n" + + "\tst1 ascii static,\n" + + "\treg1 ascii,\n" + + "\treg2 frozen<list<varint>>,\n" + + "\treg3 map<ascii, varint>,\n" + + "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" + + "\tWITH ID = " + cfs.metadata.id + "\n" + + "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)")); + } + + @Test + public void testCfmOptionsCQL() + { + String keyspace = "cql_test_keyspace_options"; + String table = "test_table_options"; + + TableMetadata.Builder builder = TableMetadata.builder(keyspace, table); + builder.addPartitionKeyColumn("pk1", IntegerType.instance) + .addClusteringColumn("cl1", IntegerType.instance) + .addRegularColumn("reg1", AsciiType.instance) + .bloomFilterFpChance(1.0) + .comment("comment") + .compaction(CompactionParams.lcs(Collections.singletonMap("sstable_size_in_mb", "1"))) + .compression(CompressionParams.lz4(1 << 16, 1 << 15)) + .dcLocalReadRepairChance(0.2) + .crcCheckChance(0.3) + .defaultTimeToLive(4) + .gcGraceSeconds(5) + .minIndexInterval(6) + .maxIndexInterval(7) + .memtableFlushPeriod(8) + .readRepairChance(0.9) + .speculativeRetry(AlwaysSpeculativeRetryPolicy.INSTANCE) + .extensions(ImmutableMap.of("ext1", ByteBuffer.wrap("val1".getBytes()))) + .recordColumnDrop(ColumnMetadata.regularColumn(keyspace, table, "reg1", AsciiType.instance), + FBUtilities.timestampMicros()); + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + assertTrue(TableCQLHelper.getTableMetadataAsCQL(cfs.metadata(), true).endsWith( + "AND bloom_filter_fp_chance = 1.0\n" + + "\tAND dclocal_read_repair_chance = 0.2\n" + + "\tAND crc_check_chance = 0.3\n" + + "\tAND default_time_to_live = 4\n" + + "\tAND gc_grace_seconds = 5\n" + + "\tAND min_index_interval = 6\n" + + "\tAND max_index_interval = 7\n" + + "\tAND memtable_flush_period_in_ms = 8\n" + + "\tAND read_repair_chance = 0.9\n" + + "\tAND speculative_retry = 'ALWAYS'\n" + + "\tAND comment = 'comment'\n" + + "\tAND caching = { 'keys': 'ALL', 'rows_per_partition': 'NONE' }\n" + + "\tAND compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'sstable_size_in_mb': '1' }\n" + + "\tAND compression = { 'chunk_length_in_kb': '64', 'min_compress_ratio': '2.0', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor' }\n" + + "\tAND cdc = false\n" + + "\tAND extensions = { 'ext1': 0x76616c31 };" + )); + } + + @Test + public void testCfmIndexJson() + { + String keyspace = "cql_test_keyspace_3"; + String table = "test_table_3"; + + TableMetadata.Builder builder = + TableMetadata.builder(keyspace, table) + .addPartitionKeyColumn("pk1", IntegerType.instance) + .addClusteringColumn("cl1", IntegerType.instance) + .addRegularColumn("reg1", AsciiType.instance); + + ColumnIdentifier reg1 = ColumnIdentifier.getInterned("reg1", true); + + builder.indexes( + Indexes.of(IndexMetadata.fromIndexTargets( + Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.VALUES)), + "indexName", + IndexMetadata.Kind.COMPOSITES, + Collections.emptyMap()), + IndexMetadata.fromIndexTargets( + Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS)), + "indexName2", + IndexMetadata.Kind.COMPOSITES, + Collections.emptyMap()), + IndexMetadata.fromIndexTargets( + Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS_AND_VALUES)), + "indexName3", + IndexMetadata.Kind.COMPOSITES, + Collections.emptyMap()), + IndexMetadata.fromIndexTargets( + Collections.singletonList(new IndexTarget(reg1, IndexTarget.Type.KEYS_AND_VALUES)), + "indexName4", + IndexMetadata.Kind.CUSTOM, + Collections.singletonMap(IndexTarget.CUSTOM_INDEX_OPTION_NAME, SASIIndex.class.getName())))); + + + SchemaLoader.createKeyspace(keyspace, KeyspaceParams.simple(1), builder); + + ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table); + + assertEquals(ImmutableList.of("CREATE INDEX \"indexName\" ON cql_test_keyspace_3.test_table_3 (values(reg1));", + "CREATE INDEX \"indexName2\" ON cql_test_keyspace_3.test_table_3 (keys(reg1));", + "CREATE INDEX \"indexName3\" ON cql_test_keyspace_3.test_table_3 (entries(reg1));", + "CREATE CUSTOM INDEX \"indexName4\" ON cql_test_keyspace_3.test_table_3 (entries(reg1)) USING 'org.apache.cassandra.index.sasi.SASIIndex';"), + TableCQLHelper.getIndexesAsCQL(cfs.metadata())); + } + + private final static String SNAPSHOT = "testsnapshot"; + + @Test + public void testSnapshot() throws Throwable + { + String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);"); + String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);"); + String typeC = createType("CREATE TYPE %s (c1 frozen<" + typeB + ">, c2 frozen<" + typeB + ">, c3 frozen<" + typeB + ">);"); + + String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" + + "pk1 varint," + + "pk2 ascii," + + "ck1 varint," + + "ck2 varint," + + "reg1 " + typeC + "," + + "reg2 int," + + "reg3 int," + + "PRIMARY KEY ((pk1, pk2), ck1, ck2)) WITH " + + "CLUSTERING ORDER BY (ck1 ASC, ck2 DESC);"); + + alterTable("ALTER TABLE %s DROP reg3 USING TIMESTAMP 10000;"); + alterTable("ALTER TABLE %s ADD reg3 int;"); + + for (int i = 0; i < 10; i++) + execute("INSERT INTO %s (pk1, pk2, ck1, ck2, reg1, reg2) VALUES (?, ?, ?, ?, ?, ?)", i, i + 1, i + 2, i + 3, null, i + 5); + + ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName); + cfs.snapshot(SNAPSHOT); + + String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset()); + assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (a1 varint, a2 varint, a3 varint);", keyspace(), typeA))); + assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (a1 varint, a2 varint, a3 varint);", keyspace(), typeA))); + assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (b1 frozen<%s>, b2 frozen<%s>, b3 frozen<%s>);", keyspace(), typeB, typeA, typeA, typeA))); + assertTrue(schema.contains(String.format("CREATE TYPE %s.%s (c1 frozen<%s>, c2 frozen<%s>, c3 frozen<%s>);", keyspace(), typeC, typeB, typeB, typeB))); + + schema = schema.substring(schema.indexOf("CREATE TABLE")); // trim to ensure order + + assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" + + "\tpk1 varint,\n" + + "\tpk2 ascii,\n" + + "\tck1 varint,\n" + + "\tck2 varint,\n" + + "\treg2 int,\n" + + "\treg3 int,\n" + + "\treg1 " + typeC + ",\n" + + "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" + + "\tWITH ID = " + cfs.metadata.id + "\n" + + "\tAND CLUSTERING ORDER BY (ck1 ASC, ck2 DESC)")); + + schema = schema.substring(schema.indexOf("ALTER")); + assertTrue(schema.startsWith(String.format("ALTER TABLE %s.%s DROP reg3 USING TIMESTAMP 10000;", keyspace(), tableName))); + assertTrue(schema.contains(String.format("ALTER TABLE %s.%s ADD reg3 int;", keyspace(), tableName))); + + JSONObject manifest = (JSONObject) new JSONParser().parse(new FileReader(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT))); + JSONArray files = (JSONArray) manifest.get("files"); + Assert.assertEquals(1, files.size()); + } + + @Test + public void testSystemKsSnapshot() throws Throwable + { + ColumnFamilyStore cfs = Keyspace.open("system").getColumnFamilyStore("peers"); + cfs.snapshot(SNAPSHOT); + + Assert.assertTrue(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT).exists()); + Assert.assertFalse(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT).exists()); + } + + @Test + public void testDroppedType() throws Throwable + { + String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);"); + String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);"); + + String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" + + "pk1 varint," + + "ck1 varint," + + "reg1 " + typeB + "," + + "reg2 varint," + + "PRIMARY KEY (pk1, ck1));"); + + alterTable("ALTER TABLE %s DROP reg1 USING TIMESTAMP 10000;"); + + Runnable validate = () -> { + try + { + ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName); + cfs.snapshot(SNAPSHOT); + String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset()); + + // When both column and it's type are dropped, the type in column definition gets substituted with a tuple + assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" + + "\tpk1 varint,\n" + + "\tck1 varint,\n" + + "\treg2 varint,\n" + + "\treg1 frozen<tuple<frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>>>,\n" + + "\tPRIMARY KEY (pk1, ck1))")); + assertTrue(schema.contains("ALTER TABLE " + keyspace() + "." + tableName + " DROP reg1 USING TIMESTAMP 10000;")); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + }; + + // Validate before and after the type drop + validate.run(); + schemaChange("DROP TYPE " + keyspace() + "." + typeB); + schemaChange("DROP TYPE " + keyspace() + "." + typeA); + validate.run(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
