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]

Reply via email to