Support CREATE TABLE LIKE with INDEXES

patch by Maxwell Guo; reviewed by Stefan Miklosovic, Sam Tunnicliffe for CASSANDRA-19965
This commit is contained in:
Maxwell Guo 2025-01-25 00:27:51 +08:00
parent ffdc4b79a4
commit 989f0414b7
13 changed files with 424 additions and 173 deletions

View File

@ -1,4 +1,5 @@
5.1
* Support CREATE TABLE LIKE WITH INDEXES (CASSANDRA-19965)
* Invalidate relevant prepared statements on every change to TableMetadata (CASSANDRA-20318)
* Add per type max size guardrails (CASSANDRA-19677)
* Make it possible to abort all kinds of multi step operations (CASSANDRA-20217)

View File

@ -414,16 +414,19 @@ bc(syntax)..
<copy-table-stmt> ::= CREATE ( TABLE | COLUMNFAMILY ) ( IF NOT EXISTS )? <newtablename> LIKE <oldtablename>
( WITH <option> ( AND <option>)* )?
<option> ::= <property>
<option> ::= <property> | INDEXES
p.
p.
__Sample:__
bc(sample)..
CREATE TABLE newtb1 LIKE oldtb;
CREATE TABLE newtb2 LIKE oldtb WITH compaction = { 'class' : 'LeveledCompactionStrategy' };
p.
CREATE TABLE newtb4 LIKE oldtb WITH INDEXES AND compaction = { 'class' : 'LeveledCompactionStrategy' };
p.
The @COPY TABLE@ statement creates a new table which is a clone of old table. The new table have the same column numbers, column names, column data types, column data mask with the old table. The new table is defined by a "name":#copyNewTableName, and the name of the old table being cloned is defined by a "name":#copyOldTableName . The table options of the new table can be defined by setting "copyoptions":#copyTableOptions. Note that the @CREATE COLUMNFAMILY LIKE@ syntax is supported as an alias for @CREATE TABLE like@ (for historical reasons).
Attempting to create an already existing table will return an error unless the @IF NOT EXISTS@ option is used. If it is used, the statement will be a no-op if the table already exists.
@ -438,7 +441,7 @@ The old table name defines the already existed table.
h4(#copyTableOptions). @<copyoptions>@
The @COPY TABLE@ statement supports a number of options that controls the configuration of a new table. These options can be specified after the @WITH@ keyword, and all options are the same as those options when creating a table except for id .
The @COPY TABLE@ statement supports a number of options that controls the configuration of a new table. These options can be specified after the @WITH@ keyword, and all options are the same as those options when creating a table except for id. Besides the options can also be specified with keyword INDEXES which means copy source table's indexes.
h3(#alterTableStmt). ALTER TABLE

View File

@ -1,3 +1,3 @@
create_table_statement::= CREATE TABLE [ IF NOT EXISTS ] new_table_name LIKE old_table_name
[ WITH table_options ]
table_options::= options [ AND table_options ]
table_options::= INDEXES | options [ AND table_options ]

View File

@ -12,3 +12,6 @@ CREATE TABLE newtb3 LIKE oldtb WITH compaction = { 'class' : 'LeveledCompactionS
AND compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 }
AND cdc = true;
CREATE TABLE newtb4 LIKE oldtb WITH INDEXES;
CREATE TABLE newtb6 LIKE oldtb WITH INDEXES AND compaction = { 'class' : 'LeveledCompactionStrategy' };

View File

@ -387,6 +387,9 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
( ender="," [propmapkey]=<term> ":" [propmapval]=<term> )*
ender="}"
;
<propertyOrOption> ::= <property>
| "INDEXES"
;
'''
@ -1314,7 +1317,7 @@ syntax_rules += r'''
<copyTableStatement> ::= "CREATE" wat=("COLUMNFAMILY" | "TABLE" ) ("IF" "NOT" "EXISTS")?
( tks=<nonSystemKeyspaceName> dot="." )? tcf=<cfOrKsName>
"LIKE" ( sks=<nonSystemKeyspaceName> dot="." )? scf=<cfOrKsName>
( "WITH" <property> ( "AND" <property> )* )?
( "WITH" <propertyOrOption> ( "AND" <propertyOrOption> )* )?
;
'''

View File

@ -796,9 +796,9 @@ class TestCqlshCompletion(CqlshCompletionCase):
choices=['<new_table_name>'])
self.trycompletions('CREATE TABLE ' + quoted_keyspace + '.new_table L',
immediate='IKE ')
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table W',
self.trycompletions('CREATE TABLE new_table LIKE old_table W',
immediate='ITH ')
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH ',
self.trycompletions('CREATE TABLE new_table LIKE old_table WITH ',
choices=['allow_auto_snapshot',
'bloom_filter_fp_chance', 'compaction',
'compression',
@ -808,23 +808,16 @@ class TestCqlshCompletion(CqlshCompletionCase):
'memtable',
'memtable_flush_period_in_ms',
'caching', 'comment',
'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH ',
choices=['allow_auto_snapshot',
'bloom_filter_fp_chance', 'compaction',
'compression',
'default_time_to_live', 'gc_grace_seconds',
'incremental_backups',
'max_index_interval',
'memtable',
'memtable_flush_period_in_ms',
'caching', 'comment',
'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
'min_index_interval',
'speculative_retry', 'additional_write_policy',
'cdc', 'read_repair',
'INDEXES'])
self.trycompletions('CREATE TABLE new_table LIKE old_table WITH INDEXES ',
choices=[';' , '=', 'AND'])
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH bloom_filter_fp_chance ',
immediate='= ')
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH bloom_filter_fp_chance = ',
choices=['<float_between_0_and_1>'])
self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH compaction ',
immediate="= {'class': '")
self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
@ -868,7 +861,10 @@ class TestCqlshCompletion(CqlshCompletionCase):
'memtable',
'memtable_flush_period_in_ms',
'caching', 'comment',
'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
'min_index_interval',
'speculative_retry', 'additional_write_policy',
'cdc', 'read_repair',
'INDEXES'])
self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
+ "{'class': 'TimeWindowCompactionStrategy', '",
choices=['compaction_window_unit', 'compaction_window_size',

View File

@ -98,6 +98,7 @@ K_TABLES: ( C O L U M N F A M I L I E S
K_MATERIALIZED:M A T E R I A L I Z E D;
K_VIEW: V I E W;
K_INDEX: I N D E X;
K_INDEXES: I N D E X E S;
K_CUSTOM: C U S T O M;
K_ON: O N;
K_TO: T O;

View File

@ -836,7 +836,16 @@ copyTableStatement returns [CopyTableStatement.Raw stmt]
: K_CREATE K_COLUMNFAMILY (K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
newCf=columnFamilyName K_LIKE oldCf=columnFamilyName
{ $stmt = new CopyTableStatement.Raw(newCf, oldCf, ifNotExists); }
( K_WITH property[stmt.attrs] ( K_AND property[stmt.attrs] )*)?
( K_WITH propertyOrOption[stmt] ( K_AND propertyOrOption[stmt] )*)?
;
propertyOrOption[CopyTableStatement.Raw stmt]
: tableLikeSingleOption[stmt]
| property[stmt.attrs]
;
tableLikeSingleOption[CopyTableStatement.Raw stmt]
: K_INDEXES {$stmt.withLikeOption(CopyTableStatement.CreateLikeOption.INDEXES);}
;
/**
@ -2084,5 +2093,6 @@ basic_unreserved_keyword returns [String str]
| K_ANN
| K_BETWEEN
| K_CHECK
| K_INDEXES
) { $str = $k.text; }
;

View File

@ -19,6 +19,8 @@
package org.apache.cassandra.cql3.statements.schema;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -35,6 +37,9 @@ import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.db.marshal.VectorType;
import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.index.sai.StorageAttachedIndex;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.schema.Indexes;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.Keyspaces;
@ -46,6 +51,7 @@ import org.apache.cassandra.schema.TableParams;
import org.apache.cassandra.schema.Triggers;
import org.apache.cassandra.schema.UserFunctions;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.service.reads.repair.ReadRepairStrategy;
import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
@ -61,12 +67,14 @@ public final class CopyTableStatement extends AlterSchemaStatement
private final String targetTableName;
private final boolean ifNotExists;
private final TableAttributes attrs;
private final CreateLikeOption createLikeOption;
public CopyTableStatement(String sourceKeyspace,
String targetKeyspace,
String sourceTableName,
String targetTableName,
boolean ifNotExists,
CreateLikeOption createLikeOption,
TableAttributes attrs)
{
super(targetKeyspace);
@ -75,6 +83,7 @@ public final class CopyTableStatement extends AlterSchemaStatement
this.sourceTableName = sourceTableName;
this.targetTableName = targetTableName;
this.ifNotExists = ifNotExists;
this.createLikeOption = createLikeOption;
this.attrs = attrs;
}
@ -196,6 +205,7 @@ public final class CopyTableStatement extends AlterSchemaStatement
TableParams originalParams = targetBuilder.build().params;
TableParams newTableParams = attrs.asAlteredTableParams(originalParams);
maybeCopyIndexes(targetBuilder, sourceTableMeta, targetKeyspaceMeta);
TableMetadata table = targetBuilder.params(newTableParams)
.id(TableId.get(metadata))
@ -229,12 +239,59 @@ public final class CopyTableStatement extends AlterSchemaStatement
validateDefaultTimeToLive(attrs.asNewTableParams());
}
private void maybeCopyIndexes(TableMetadata.Builder builder, TableMetadata sourceTableMeta, KeyspaceMetadata targetKeyspaceMeta)
{
if (createLikeOption != CreateLikeOption.INDEXES || sourceTableMeta.indexes.isEmpty())
return;
Set<String> customIndexes = Sets.newTreeSet();
List<IndexMetadata> indexesToCopy = new ArrayList<>();
for (IndexMetadata indexMetadata : sourceTableMeta.indexes)
{
// only sai and legacy secondary index is supported
if (indexMetadata.isCustom() && !StorageAttachedIndex.class.getCanonicalName().equals(indexMetadata.getIndexClassName()))
{
customIndexes.add(indexMetadata.name);
continue;
}
ColumnMetadata targetColumn = sourceTableMeta.getColumn(UTF8Type.instance.decompose(indexMetadata.options.get("target")));
String indexName;
// The rules for generating the index names of the target table are:
// (1) If the source table's index names follow the pattern sourcetablename_columnname_idx_number, the index names are considered to be generated by the system,
// then we directly replace the name of source table with the name of target table, and increment the number after idx to avoid index name conflicts.
// (2) Index names that do not follow the above pattern are considered user-defined, so the index names are retained and increment the number after idx to avoid conflicts.
if (indexMetadata.name.startsWith(sourceTableName + "_" + targetColumn.name + "_idx"))
{
String baseName = IndexMetadata.generateDefaultIndexName(targetTableName, targetColumn.name);
indexName = targetKeyspaceMeta.findAvailableIndexName(baseName, indexesToCopy, targetKeyspaceMeta);
}
else
{
indexName = targetKeyspaceMeta.findAvailableIndexName(indexMetadata.name, indexesToCopy, targetKeyspaceMeta);
}
indexesToCopy.add(IndexMetadata.fromSchemaMetadata(indexName, indexMetadata.kind, indexMetadata.options));
}
if (!indexesToCopy.isEmpty())
builder.indexes(Indexes.builder().add(indexesToCopy).build());
if (!customIndexes.isEmpty())
ClientWarn.instance.warn(String.format("Source table %s.%s to copy indexes from to %s.%s has custom indexes. These indexes were not copied: %s",
sourceKeyspace,
sourceTableName,
targetKeyspace,
targetTableName,
customIndexes));
}
public final static class Raw extends CQLStatement.Raw
{
private final QualifiedName oldName;
private final QualifiedName newName;
private final boolean ifNotExists;
public final TableAttributes attrs = new TableAttributes();
private CreateLikeOption createLikeOption = null;
public Raw(QualifiedName newName, QualifiedName oldName, boolean ifNotExists)
{
@ -248,7 +305,17 @@ public final class CopyTableStatement extends AlterSchemaStatement
{
String oldKeyspace = oldName.hasKeyspace() ? oldName.getKeyspace() : state.getKeyspace();
String newKeyspace = newName.hasKeyspace() ? newName.getKeyspace() : state.getKeyspace();
return new CopyTableStatement(oldKeyspace, newKeyspace, oldName.getName(), newName.getName(), ifNotExists, attrs);
return new CopyTableStatement(oldKeyspace, newKeyspace, oldName.getName(), newName.getName(), ifNotExists, createLikeOption, attrs);
}
public void withLikeOption(CreateLikeOption option)
{
this.createLikeOption = option;
}
}
public enum CreateLikeOption
{
INDEXES;
}
}

View File

@ -18,6 +18,8 @@
package org.apache.cassandra.schema;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@ -207,19 +209,49 @@ public final class KeyspaceMetadata implements SchemaElement
public String findAvailableIndexName(String baseName)
{
if (!hasIndex(baseName))
return findAvailableIndexName(baseName, Collections.emptySet(), this);
}
/**
* find an avaiable index name based on the indexes in target keyspace and indexes collections
* @param baseName the base name of index
* @param indexes find out whether there is any conflict with baseName in the indexes
* @param keyspaceMetadata find out whether there is any conflict with baseName in keyspaceMetadata
* */
public String findAvailableIndexName(String baseName, Collection<IndexMetadata> indexes, KeyspaceMetadata keyspaceMetadata)
{
if (!hasIndex(baseName, indexes, keyspaceMetadata))
return baseName;
int i = 1;
do
{
String name = baseName + '_' + i++;
if (!hasIndex(name))
String name = generateIndexName(baseName);
if (!hasIndex(name, indexes, keyspaceMetadata))
return name;
baseName = name;
}
while (true);
}
private String generateIndexName(String baseName)
{
if (baseName.matches(".*_\\d+$"))
{
int lastUnderscoreIndex = baseName.lastIndexOf('_');
String numberStr = baseName.substring(lastUnderscoreIndex + 1);
int number = Integer.parseInt(numberStr) + 1;
return baseName.substring(0, lastUnderscoreIndex + 1) + number;
}
return baseName + "_1";
}
private boolean hasIndex(String baseName, Collection<IndexMetadata> indexes, KeyspaceMetadata keyspaceMetadata)
{
return any(indexes, t -> t.name.equals(baseName)) ||
any(keyspaceMetadata.tables, t -> t.indexes.has(baseName));
}
public Optional<TableMetadata> findIndexedTable(String indexName)
{
for (TableMetadata table : tablesAndViews())

View File

@ -40,7 +40,8 @@ public class AlterSchemaStatementTest extends CQLTester
"CREATE TABLE ks.t1 (k int PRIMARY KEY)",
"ALTER MATERIALIZED VIEW ks.v1 WITH compaction = { 'class' : 'LeveledCompactionStrategy' }",
"ALTER TABLE ks.t1 ADD v int",
"CREATE TABLE ks.tb like ks1.tb"
"CREATE TABLE ks.tb like ks1.tb",
"CREATE TABLE ks.tb like ks1.tb WITH indexes"
};
private final ClientState clientState = ClientState.forExternalCalls(InetSocketAddress.createUnresolved("127.0.0.1", 1234));

View File

@ -1095,7 +1095,7 @@ public abstract class CQLTester
}
String currentTable = createTableName(targetTable);
String fullQuery = currentTable == null ? query : String.format(query, targetKeyspace + "." + currentTable, sourceKeyspace + "." + sourceTable);;
String fullQuery = currentTable == null ? query : String.format(query, targetKeyspace + "." + currentTable, sourceKeyspace + "." + sourceTable);
logger.info(fullQuery);
schemaChange(fullQuery);
return currentTable;
@ -1972,21 +1972,41 @@ public abstract class CQLTester
* Determine whether the source and target TableMetadata is equal without compare the table name and dropped columns.
* @param source the source TableMetadata
* @param target the target TableMetadata
* @param compareParams wether compare table params
* @param compareParams wether compare table's params
* @param compareIndexes wether compare table's indexes
* @param compareTrigger wether compare table's triggers
* @param compareIndexWithOutName wether ignore indexes' name when doing index comparison
* if true then compare the index without name
* */
protected boolean equalsWithoutTableNameAndDropCns(TableMetadata source, TableMetadata target, boolean compareParams, boolean compareIndexes, boolean compareTrigger)
protected boolean equalsWithoutTableNameAndDropCns(TableMetadata source, TableMetadata target, boolean compareParams, boolean compareIndexes, boolean compareIndexWithOutName)
{
return source.partitioner.equals(target.partitioner)
&& source.kind == target.kind
&& source.flags.equals(target.flags)
&& (!compareParams || source.params.equals(target.params))
&& (!compareIndexes || source.indexes.equals(target.indexes))
&& (!compareTrigger || source.triggers.equals(target.triggers))
&& (!compareIndexes || compareIndexes(source, target, compareIndexWithOutName))
&& columnsEqualWitoutKsTb(source, target);
}
private boolean compareIndexes(TableMetadata source, TableMetadata target, boolean compareIndexWithOutName)
{
if (compareIndexWithOutName)
{
if (source.indexes.size() != target.indexes.size())
return false;
Iterator<IndexMetadata> leftIter = source.indexes.stream().sorted(Comparator.comparing(idx -> idx.name)).iterator();
Iterator<IndexMetadata> rightIter = target.indexes.stream().sorted(Comparator.comparing(idx -> idx.name)).iterator();
boolean result = true;
while (leftIter.hasNext() && rightIter.hasNext())
{
IndexMetadata left = leftIter.next();
IndexMetadata right = rightIter.next();
result &= right.equalsWithoutName(left);
}
return result;
}
return source.indexes.equals(target.indexes);
}
// only compare columns
private boolean columnsEqualWitoutKsTb(TableMetadata source, TableMetadata target)
{

View File

@ -20,11 +20,13 @@ package org.apache.cassandra.schema.createlike;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
@ -34,14 +36,16 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import com.datastax.driver.core.ResultSet;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.cql3.validation.operations.CreateTest;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.compaction.LeveledCompactionStrategy;
import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.index.StubIndex;
import org.apache.cassandra.schema.CachingParams;
import org.apache.cassandra.schema.CompactionParams;
import org.apache.cassandra.schema.CompressionParams;
@ -54,6 +58,7 @@ import org.apache.cassandra.service.reads.repair.ReadRepairStrategy;
import org.apache.cassandra.utils.TimeUUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -68,28 +73,27 @@ public class CreateLikeTest extends CQLTester
public static Collection<Object[]> data()
{
List<Object[]> result = new ArrayList<>();
result.add(new Object[]{false});
result.add(new Object[]{true});
result.add(new Object[]{ false });
result.add(new Object[]{ true });
return result;
}
private UUID uuid1 = UUID.fromString("62c3e96f-55cd-493b-8c8e-5a18883a1698");
private UUID uuid2 = UUID.fromString("52c3e96f-55cd-493b-8c8e-5a18883a1698");
private TimeUUID timeUuid1 = TimeUUID.fromString("00346642-2d2f-11ed-a261-0242ac120002");
private TimeUUID timeUuid2 = TimeUUID.fromString("10346642-2d2f-11ed-a261-0242ac120002");
private Duration duration1 = Duration.newInstance(1, 2, 3);
private Duration duration2 = Duration.newInstance(1, 2, 4);
private Date date1 = new Date();
private Double d1 = Double.valueOf("1.1");
private Double d2 = Double.valueOf("2.2");
private Float f1 = Float.valueOf("3.33");
private Float f2 = Float.valueOf("4.44");
private BigDecimal decimal1 = BigDecimal.valueOf(1.1);
private BigDecimal decimal2 = BigDecimal.valueOf(2.2);
private Vector<Integer> vector1 = vector(1, 2);
private Vector<Integer> vector2 = vector(3, 4);
private String keyspace1 = "keyspace1";
private String keyspace2 = "keyspace2";
private final UUID uuid1 = UUID.fromString("62c3e96f-55cd-493b-8c8e-5a18883a1698");
private final UUID uuid2 = UUID.fromString("52c3e96f-55cd-493b-8c8e-5a18883a1698");
private final TimeUUID timeUuid1 = TimeUUID.fromString("00346642-2d2f-11ed-a261-0242ac120002");
private final TimeUUID timeUuid2 = TimeUUID.fromString("10346642-2d2f-11ed-a261-0242ac120002");
private final Duration duration1 = Duration.newInstance(1, 2, 3);
private final Duration duration2 = Duration.newInstance(1, 2, 4);
private final Double d1 = Double.valueOf("1.1");
private final Double d2 = Double.valueOf("2.2");
private final Float f1 = Float.valueOf("3.33");
private final Float f2 = Float.valueOf("4.44");
private final BigDecimal decimal1 = BigDecimal.valueOf(1.1);
private final BigDecimal decimal2 = BigDecimal.valueOf(2.2);
private final Vector<Integer> vector1 = vector(1, 2);
private final Vector<Integer> vector2 = vector(3, 4);
private final String keyspace1 = "keyspace1";
private final String keyspace2 = "keyspace2";
private String sourceKs;
private String targetKs;
@ -139,38 +143,38 @@ public class CreateLikeTest extends CQLTester
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
execute("INSERT INTO " + sourceKs + "." + sourceTb + " (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)",
1, set(list("1", "2"), list("3", "4")), map("k", 1), (short)2, duration1, (byte)4);
1, set(list("1", "2"), list("3", "4")), map("k", 1), (short) 2, duration1, (byte) 4);
execute("INSERT INTO " + targetKs + "." + targetTb + " (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)",
2, set(list("5", "6"), list("7", "8")), map("nk", 2), (short)3, duration2, (byte)5);
2, set(list("5", "6"), list("7", "8")), map("nk", 2), (short) 3, duration2, (byte) 5);
assertRows(execute("SELECT * FROM " + sourceKs + "." + sourceTb),
row(1, set(list("1", "2"), list("3", "4")), map("k", 1), (short)2, duration1, (byte)4));
row(1, set(list("1", "2"), list("3", "4")), map("k", 1), (short) 2, duration1, (byte) 4));
assertRows(execute("SELECT * FROM " + targetKs + "." + targetTb),
row(2, set(list("5", "6"), list("7", "8")), map("nk", 2), (short)3, duration2, (byte)5));
row(2, set(list("5", "6"), list("7", "8")), map("nk", 2), (short) 3, duration2, (byte) 5));
sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int , b double, c tinyint, d float, e list<text>, f map<text, int>, g duration, PRIMARY KEY((a, b, c), d));");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
execute("INSERT INTO " + sourceKs + "." + sourceTb + " (a, b, c, d, e, f, g) VALUES (?, ?, ?, ?, ?, ?, ?) ",
1, d1, (byte)4, f1, list("a", "b"), map("k", 1), duration1);
1, d1, (byte) 4, f1, list("a", "b"), map("k", 1), duration1);
execute("INSERT INTO " + targetKs + "." + targetTb + " (a, b, c, d, e, f, g) VALUES (?, ?, ?, ?, ?, ?, ?) ",
2, d2, (byte)5, f2, list("c", "d"), map("nk", 2), duration2);
2, d2, (byte) 5, f2, list("c", "d"), map("nk", 2), duration2);
assertRows(execute("SELECT * FROM " + sourceKs + "." + sourceTb),
row(1, d1, (byte)4, f1, list("a", "b"), map("k", 1), duration1));
row(1, d1, (byte) 4, f1, list("a", "b"), map("k", 1), duration1));
assertRows(execute("SELECT * FROM " + targetKs + "." + targetTb),
row(2, d2, (byte)5, f2, list("c", "d"), map("nk", 2), duration2));
row(2, d2, (byte) 5, f2, list("c", "d"), map("nk", 2), duration2));
sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int , " +
"b text, " +
"c bigint, " +
"d decimal, " +
"e set<text>, " +
"f uuid, " +
"g vector<int, 2>, " +
"h list<float>, " +
"i timeuuid, " +
"j map<text, frozen<set<int>>>, " +
"PRIMARY KEY((a, b), c, d)) " +
"WITH CLUSTERING ORDER BY (c DESC, d ASC);");
"b text, " +
"c bigint, " +
"d decimal, " +
"e set<text>, " +
"f uuid, " +
"g vector<int, 2>, " +
"h list<float>, " +
"i timeuuid, " +
"j map<text, frozen<set<int>>>, " +
"PRIMARY KEY((a, b), c, d)) " +
"WITH CLUSTERING ORDER BY (c DESC, d ASC);");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
execute("INSERT INTO " + sourceKs + "." + sourceTb + " (a, b, c, d, e, f, g, h, i, j) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
@ -207,15 +211,15 @@ public class CreateLikeTest extends CQLTester
String targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " DROP d");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " DROP d");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " ADD e uuid");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " ADD e uuid");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " ADD f float");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " ADD f float");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
@ -226,15 +230,15 @@ public class CreateLikeTest extends CQLTester
assertRows(execute("SELECT * FROM " + targetKs + "." + targetTb),
row(2, "2", duration2, uuid2, f2));
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " DROP f USING TIMESTAMP 20000");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " DROP f USING TIMESTAMP 20000");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " RENAME b TO bb ");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " RENAME b TO bb ");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
alterTable("ALTER TABLE " + sourceKs + " ." + sourceTb + " WITH compaction = {'class':'LeveledCompactionStrategy', 'sstable_size_in_mb':10, 'fanout_size':16} ");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " WITH compaction = {'class':'LeveledCompactionStrategy', 'sstable_size_in_mb' : 10, 'fanout_size' : 16} ");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
@ -244,7 +248,6 @@ public class CreateLikeTest extends CQLTester
row(1, "1", duration1, uuid1));
assertRows(execute("SELECT * FROM " + targetKs + "." + targetTb),
row(2, "2", duration2, uuid2));
}
@Test
@ -252,52 +255,52 @@ public class CreateLikeTest extends CQLTester
{
// compression
String tbCompressionDefault1 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))");
String tbCompressionDefault2 =createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'enabled' : 'false'};");
String tbCompressionDefault2 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'enabled' : 'false'};");
String tbCompressionSnappy1 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };");
String tbCompressionSnappy2 =createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32, 'enabled' : true };");
String tbCompressionSnappy3 = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 2 };");
String tbCompressionSnappy4 = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 1 };");
String tbCompressionSnappy5 = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 0 };");
" WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };");
String tbCompressionSnappy2 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32, 'enabled' : true };");
String tbCompressionSnappy3 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 2 };");
String tbCompressionSnappy4 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 1 };");
String tbCompressionSnappy5 = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 0 };");
// memtable
String tableMemtableSkipList = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'skiplist';");
String tableMemtableTrie = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'trie';");
String tableMemtableDefault = createTable(sourceKs,"CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'default';");
String tableMemtableSkipList = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'skiplist';");
String tableMemtableTrie = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'trie';");
String tableMemtableDefault = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))" +
" WITH memtable = 'default';");
// compaction
String tableCompactionStcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class':'SizeTieredCompactionStrategy', 'min_threshold':2, 'enabled':false};");
String tableCompactionLcs =createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class':'LeveledCompactionStrategy', 'sstable_size_in_mb':1, 'fanout_size':5};");
String tableCompactionTwcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class':'TimeWindowCompactionStrategy', 'min_threshold':2};");
String tableCompactionUcs =createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class':'UnifiedCompactionStrategy'};");
String tableCompactionStcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 2, 'enabled' : false};");
String tableCompactionLcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class' : 'LeveledCompactionStrategy', 'sstable_size_in_mb' : 1, 'fanout_size' : 5};");
String tableCompactionTwcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class' : 'TimeWindowCompactionStrategy', 'min_threshold' : 2};");
String tableCompactionUcs = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH compaction = {'class' : 'UnifiedCompactionStrategy'};");
// other options are all different from default
String tableOtherOptions = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH" +
" additional_write_policy = '95p' " +
" AND bloom_filter_fp_chance = 0.1 " +
" AND caching = {'keys': 'ALL', 'rows_per_partition': '100'}" +
" AND cdc = true " +
" AND comment = 'test for create like'" +
" AND crc_check_chance = 0.1" +
" AND default_time_to_live = 10" +
" AND compaction = {'class':'UnifiedCompactionStrategy'} " +
" AND compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 }" +
" AND gc_grace_seconds = 100" +
" AND incremental_backups = false" +
" AND max_index_interval = 1024" +
" AND min_index_interval = 64" +
" AND speculative_retry = '95p'" +
" AND read_repair = 'NONE'" +
" AND memtable_flush_period_in_ms = 360000" +
" AND memtable = 'default';" );
" additional_write_policy = '95p' " +
" AND bloom_filter_fp_chance = 0.1 " +
" AND caching = {'keys' : 'ALL', 'rows_per_partition' : '100'}" +
" AND cdc = true " +
" AND comment = 'test for create like'" +
" AND crc_check_chance = 0.1" +
" AND default_time_to_live = 10" +
" AND compaction = {'class' : 'UnifiedCompactionStrategy'} " +
" AND compression = {'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 }" +
" AND gc_grace_seconds = 100" +
" AND incremental_backups = false" +
" AND max_index_interval = 1024" +
" AND min_index_interval = 64" +
" AND speculative_retry = '95p'" +
" AND read_repair = 'NONE'" +
" AND memtable_flush_period_in_ms = 360000" +
" AND memtable = 'default';");
String tbLikeCompressionDefault1 = createTableLike("CREATE TABLE %s LIKE %s", tbCompressionDefault1, sourceKs, targetKs);
String tbLikeCompressionDefault2 = createTableLike("CREATE TABLE %s LIKE %s", tbCompressionDefault2, sourceKs, targetKs);
@ -313,7 +316,7 @@ public class CreateLikeTest extends CQLTester
String tbLikeCompactionLcs = createTableLike("CREATE TABLE %s LIKE %s", tableCompactionLcs, sourceKs, targetKs);
String tbLikeCompactionTwcs = createTableLike("CREATE TABLE %s LIKE %s", tableCompactionTwcs, sourceKs, targetKs);
String tbLikeCompactionUcs = createTableLike("CREATE TABLE %s LIKE %s", tableCompactionUcs, sourceKs, targetKs);
String tbLikeCompactionOthers= createTableLike("CREATE TABLE %s LIKE %s", tableOtherOptions, sourceKs, targetKs);
String tbLikeCompactionOthers = createTableLike("CREATE TABLE %s LIKE %s", tableOtherOptions, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tbCompressionDefault1, tbLikeCompressionDefault1);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tbCompressionDefault2, tbLikeCompressionDefault2);
@ -332,27 +335,28 @@ public class CreateLikeTest extends CQLTester
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tableOtherOptions, tbLikeCompactionOthers);
// a copy of the table with the table parameters set
String tableCopyAndSetCompression = createTableLike("CREATE TABLE %s LIKE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 64 };",
String tableCopyAndSetCompression = createTableLike("CREATE TABLE %s LIKE %s WITH compression = {'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 64 };",
tbCompressionSnappy1, sourceKs, targetKs);
String tableCopyAndSetLCSCompaction = createTableLike("CREATE TABLE %s LIKE %s WITH compaction = {'class':'LeveledCompactionStrategy', 'sstable_size_in_mb':10, 'fanout_size':16};",
tableCompactionLcs, sourceKs, targetKs);
String tableCopyAndSetLCSCompaction = createTableLike("CREATE TABLE %s LIKE %s WITH compaction = {'class' : 'LeveledCompactionStrategy', 'sstable_size_in_mb' : 10, 'fanout_size' : 16};",
tableCompactionLcs, sourceKs, targetKs);
String tableCopyAndSetAllParams = createTableLike("CREATE TABLE %s (a text, b int, c int, primary key (a, b)) WITH" +
" bloom_filter_fp_chance = 0.75 " +
" AND caching = {'keys': 'NONE', 'rows_per_partition': '10'}" +
" AND cdc = true " +
" AND comment = 'test for create like and set params'" +
" AND crc_check_chance = 0.8" +
" AND default_time_to_live = 100" +
" AND compaction = {'class':'SizeTieredCompactionStrategy'} " +
" AND compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 64 }" +
" AND gc_grace_seconds = 1000" +
" AND incremental_backups = true" +
" AND max_index_interval = 128" +
" AND min_index_interval = 16" +
" AND speculative_retry = '96p'" +
" AND read_repair = 'NONE'" +
" AND memtable_flush_period_in_ms = 3600;",
tableOtherOptions, sourceKs, targetKs);
" bloom_filter_fp_chance = 0.75 " +
" AND caching = {'keys' : 'NONE', 'rows_per_partition' : '10'}" +
" AND cdc = true " +
" AND comment = 'test for create like and set params'" +
" AND crc_check_chance = 0.8" +
" AND default_time_to_live = 100" +
" AND compaction = {'class' : 'SizeTieredCompactionStrategy'} " +
" AND compression = {'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 64}" +
" AND gc_grace_seconds = 1000" +
" AND incremental_backups = true" +
" AND max_index_interval = 128" +
" AND min_index_interval = 16" +
" AND speculative_retry = '96p'" +
" AND read_repair = 'NONE'" +
" AND memtable_flush_period_in_ms = 3600;",
tableOtherOptions, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tbCompressionDefault1, tableCopyAndSetCompression, false, false, false);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tableCompactionLcs, tableCopyAndSetLCSCompaction, false, false, false);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, tableOtherOptions, tableCopyAndSetAllParams, false, false, false);
@ -364,29 +368,29 @@ public class CreateLikeTest extends CQLTester
assertEquals(paramsSetLCSCompaction, TableParams.builder().compaction(CompactionParams.create(LeveledCompactionStrategy.class,
Map.of("sstable_size_in_mb", "10",
"fanout_size", "16")))
.build());
.build());
assertEquals(paramsSetAllParams, TableParams.builder().bloomFilterFpChance(0.75)
.caching(new CachingParams(false, 10))
.cdc(true)
.comment("test for create like and set params")
.crcCheckChance(0.8)
.defaultTimeToLive(100)
.compaction(CompactionParams.stcs(Collections.emptyMap()))
.compression(CompressionParams.snappy(64 * 1024, 0.0))
.gcGraceSeconds(1000)
.incrementalBackups(true)
.maxIndexInterval(128)
.minIndexInterval(16)
.speculativeRetry(SpeculativeRetryPolicy.fromString("96PERCENTILE"))
.readRepair(ReadRepairStrategy.NONE)
.memtableFlushPeriodInMs(3600)
.build());
.caching(new CachingParams(false, 10))
.cdc(true)
.comment("test for create like and set params")
.crcCheckChance(0.8)
.defaultTimeToLive(100)
.compaction(CompactionParams.stcs(Collections.emptyMap()))
.compression(CompressionParams.snappy(64 * 1024, 0.0))
.gcGraceSeconds(1000)
.incrementalBackups(true)
.maxIndexInterval(128)
.minIndexInterval(16)
.speculativeRetry(SpeculativeRetryPolicy.fromString("96PERCENTILE"))
.readRepair(ReadRepairStrategy.NONE)
.memtableFlushPeriodInMs(3600)
.build());
// table id
TableId id = TableId.generate();
String tbNormal = createTable(sourceKs, "CREATE TABLE %s (a text, b int, c int, primary key (a, b))");
assertInvalidThrowMessage("Cannot alter table id.", ConfigurationException.class,
"CREATE TABLE " + targetKs + ".targetnormal LIKE " + sourceKs + "." + tbNormal + " WITH ID = " + id);
"CREATE TABLE " + targetKs + ".targetnormal LIKE " + sourceKs + "." + tbNormal + " WITH ID = " + id);
}
@Test
@ -401,7 +405,7 @@ public class CreateLikeTest extends CQLTester
// add static column
sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b))");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " ADD d int static");
alterTable("ALTER TABLE " + sourceKs + "." + sourceTb + " ADD d int static");
targetTb = createTableLike("CREATE TABLE %s LIKE %s", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb);
}
@ -486,35 +490,35 @@ public class CreateLikeTest extends CQLTester
// source table's column's data type is udt, and its subtypes are native type and udt
String sourceTbUdtFrozen = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtFrozen + ");");
// source table's column's data type is udt, and its subtypes are native type and more than one udt
String sourceTbUdtComb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtFrozen + ", d " + udt+ ");");
String sourceTbUdtComb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtFrozen + ", d " + udt + ");");
// source table's column's data type is udt, and its subtypes are native type and more than one udt
String sourceTbUdtCombNotExist = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtFrozen + ", d " + udtFrozenNotExist+ ");");
String sourceTbUdtCombNotExist = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtFrozen + ", d " + udtFrozenNotExist + ");");
if (differentKs)
{
assertInvalidThrowMessage("UDTs " + udt + " do not exist in target keyspace '" + targetKs +"'.",
assertInvalidThrowMessage("UDTs " + udt + " do not exist in target keyspace '" + targetKs + "'.",
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudt LIKE " + sourceKs + "." + sourceTbUdt);
assertInvalidThrowMessage("UDTs " + udtSet + " do not exist in target keyspace '" + targetKs +"'.",
assertInvalidThrowMessage("UDTs " + udtSet + " do not exist in target keyspace '" + targetKs + "'.",
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbdtset LIKE " + sourceKs + "." + sourceTbUdtSet);
assertInvalidThrowMessage(String.format("UDTs %s do not exist in target keyspace '%s'.", Sets.newHashSet(udt, udtFrozen).stream().sorted().collect(Collectors.joining(", ")), targetKs),
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudtfrozen LIKE " + sourceKs + "." + sourceTbUdtFrozen);
"CREATE TABLE " + targetKs + ".tbudtfrozen LIKE " + sourceKs + "." + sourceTbUdtFrozen);
assertInvalidThrowMessage(String.format("UDTs %s do not exist in target keyspace '%s'.", Sets.newHashSet(udt, udtFrozen).stream().sorted().collect(Collectors.joining(", ")), targetKs),
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudtfrozen LIKE " + sourceKs + "." + sourceTbUdtFrozen);
assertInvalidThrowMessage(String.format("UDTs %s do not exist in target keyspace '%s'.", Sets.newHashSet(udt, udtFrozen).stream().sorted().collect(Collectors.joining(", ")), targetKs),
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudtcomb LIKE " + sourceKs + "." + sourceTbUdtComb);
assertInvalidThrowMessage(String.format("UDTs %s do not exist in target keyspace '%s'.", Sets.newHashSet(udtNew, udt,udtFrozenNotExist, udtFrozen).stream().sorted().collect(Collectors.joining(", ")), targetKs),
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudtcomb LIKE " + sourceKs + "." + sourceTbUdtCombNotExist);
assertInvalidThrowMessage(String.format("UDTs %s do not exist in target keyspace '%s'.", Sets.newHashSet(udtNew, udt, udtFrozenNotExist, udtFrozen).stream().sorted().collect(Collectors.joining(", ")), targetKs),
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbudtcomb LIKE " + sourceKs + "." + sourceTbUdtCombNotExist);
// different keyspaces with udts that have same udt name, different fields
String udtWithDifferentField = createType(sourceKs, "CREATE TYPE %s (aa int, bb text)");
createType("CREATE TYPE IF NOT EXISTS " + targetKs + "." + udtWithDifferentField + " (aa int, cc text)");
String sourceTbDiffUdt = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b duration, c " + udtWithDifferentField + ");");
assertInvalidThrowMessage("Target keyspace '" + targetKs + "' has same UDT name '"+ udtWithDifferentField +"' as source keyspace '" + sourceKs + "' but with different structure.",
assertInvalidThrowMessage("Target keyspace '" + targetKs + "' has same UDT name '" + udtWithDifferentField + "' as source keyspace '" + sourceKs + "' but with different structure.",
InvalidRequestException.class,
"CREATE TABLE " + targetKs + ".tbdiffudt LIKE " + sourceKs + "." + sourceTbDiffUdt);
}
@ -576,28 +580,138 @@ public class CreateLikeTest extends CQLTester
public void testUnSupportedSchema() throws Throwable
{
createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text)", "tb");
String index = createIndex( "CREATE INDEX ON " + sourceKs + ".tb (c)");
String index = createIndex("CREATE INDEX ON " + sourceKs + ".tb (c)");
assertInvalidThrowMessage("Souce Table '" + targetKs + "." + index + "' doesn't exist", InvalidRequestException.class,
"CREATE TABLE " + sourceKs + ".newtb LIKE " + targetKs + "." + index + ";");
"CREATE TABLE " + sourceKs + ".newtb LIKE " + targetKs + "." + index + ";");
assertInvalidThrowMessage("System keyspace 'system' is not user-modifiable", InvalidRequestException.class,
"CREATE TABLE system.local_clone LIKE system.local ;");
assertInvalidThrowMessage("System keyspace 'system_views' is not user-modifiable", InvalidRequestException.class,
"CREATE TABLE system_views.newtb LIKE system_views.snapshots ;");
}
private void assertTableMetaEqualsWithoutKs(String sourceKs, String targetKs, String sourceTb, String targetTb)
@Test
public void testTableCopyWithIndexes()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text, d int, e text, f int, g text)", "sourcetb");
createIndex(sourceKs, "CREATE INDEX ON %s (d)");
createIndex(sourceKs, "CREATE INDEX ON %s (c)");
createIndex(sourceKs, "CREATE INDEX ON %s (b) USING 'sai'");
createIndex(sourceKs, "CREATE CUSTOM INDEX ON %s (e) USING 'storageattachedindex'");
createIndex(sourceKs, "CREATE CUSTOM INDEX ON %s (f) USING 'org.apache.cassandra.index.sai.StorageAttachedIndex'");
String targetTb = createTableLike("CREATE TABLE %s LIKE %s WITH INDEXES", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, true, true);
}
private void assertTableMetaEqualsWithoutKs(String sourceKs, String targetKs, String sourceTb, String targetTb, boolean compareParams, boolean compareIndexes, boolean compareTrigger)
@Test
public void testTableCopyWithMultiIndexOnSameColumn()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text, d int, e text, f int, g text)", "sourcetb");
createIndex(sourceKs, "CREATE INDEX " + sourceTb + "_b_idx1 ON %s (b) USING 'legacy_local_table'");
createIndex(sourceKs, "CREATE INDEX " + sourceTb + "_b_idx2 ON %s (b) USING 'sai'");
String targetTb = createTableLike("CREATE TABLE %s LIKE %s WITH INDEXES", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, true, true);
}
@Test
public void testTableCopyWithOutIndexes()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int)", "sourcetb");
// not copied
createIndex(sourceKs, "CREATE CUSTOM INDEX ON %s (b) USING 'org.apache.cassandra.index.sasi.SASIIndex'");
createIndex(sourceKs, "CREATE CUSTOM INDEX testidx ON %s (b) USING '" + StubIndex.class.getName() + "'");
String targetTb = createTableLike("CREATE TABLE %s LIKE %s WITH indexes", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, false, false);
assertEquals(2, getTableMetadata(sourceKs, sourceTb).indexes.size());
assertEquals(0, getTableMetadata(targetKs, targetTb).indexes.size());
}
@Test
public void testManyTableCopyWithIndex()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c int)", "sourcetb");
createIndex(sourceKs, "CREATE INDEX myindex ON %s (b) USING 'legacy_local_table'");
createIndex(sourceKs, "CREATE INDEX myindex_1 ON %s (b) USING 'sai'");
createIndex(sourceKs, "CREATE INDEX myindex_1_1 ON %s (c) USING 'sai'");
createIndex(sourceKs, "CREATE INDEX myindex__1 ON %s (c) USING 'legacy_local_table'");
String targetTb = createTableLike("CREATE TABLE %s LIKE %s WITH indexes", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, false, false);
Set<String> resultIndexNames = getTableMetadata(targetKs, targetTb).indexes.stream().map(meta -> meta.name).collect(Collectors.toSet());
Set<String> expectedIndexNames = differentKs ? Sets.newHashSet("myindex", "myindex_1", "myindex_1_1", "myindex__1") :
Sets.newHashSet("myindex_2", "myindex_3", "myindex_1_2", "myindex__2");
assertEquals(0, Sets.difference(resultIndexNames, expectedIndexNames).size());
}
@Test
public void testTableCopyWithIndexesAndTableProperty()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text, d int)", "sourcetb");
createIndex(sourceKs, "CREATE INDEX ON %s (b)");
String targetTbWithAll = createTableLike("CREATE TABLE %s LIKE %s WITH INDEXES AND crc_check_chance = 0.8 AND cdc = true", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTbWithAll, false, true, true);
TableMetadata source = getTableMetadata(sourceKs, sourceTb);
TableMetadata target = getTableMetadata(targetKs, targetTbWithAll);
assertNotEquals(source.params, target.params);
assertTrue(target.params.cdc);
assertFalse(source.params.cdc);
assertEquals(0.8, target.params.crcCheckChance, 0.0);
}
@Test
public void testIndexesNameForCopiedTable()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text, d int, e text)", "sourcetb");
String idx1 = createIndex(sourceKs, "CREATE INDEX ON %s (d)");
String idx2 = createIndex(sourceKs, "CREATE INDEX ON %s (c)");
String idx3 = createIndex(sourceKs, "CREATE INDEX ON %s (b) USING 'sai'");
String idx4 = createIndex(sourceKs, "CREATE INDEX idx_for_e_column ON %s (e) USING 'sai'");
assertEquals(sourceTb + "_d_idx" , idx1);
assertEquals(sourceTb + "_c_idx" , idx2);
assertEquals(sourceTb + "_b_idx" , idx3);
assertEquals("idx_for_e_column", idx4);
String targetTb = createTableLike("CREATE TABLE %s LIKE %s WITH INDEXES", sourceTb, sourceKs, targetKs);
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, true, true);
TableMetadata target = getTableMetadata(targetKs, targetTb);
assertTrue(target.indexes.has(targetTb + "_d_idx"));
assertTrue(target.indexes.has(targetTb + "_c_idx"));
assertTrue(target.indexes.has(targetTb + "_b_idx"));
if (differentKs)
{
assertTrue(target.indexes.has("idx_for_e_column"));
}
else
{
// if within the same keyspace, the target table will be created with a new
// index name in the format oldindexname_number, where the number starts from 1.
assertTrue(target.indexes.has("idx_for_e_column_1"));
}
}
@Test
public void testTableCopyWithCustomIndexes()
{
String sourceTb = createTable(sourceKs, "CREATE TABLE %s (a int PRIMARY KEY, b int, c text, d int)", "sourcetb");
String idx = createIndex(sourceKs, "CREATE CUSTOM INDEX testidx ON %s (b) USING '" + StubIndex.class.getName() + "'");
Set<String> customIndexes = new TreeSet<>(Arrays.asList(idx));
ResultSet res = executeNet("CREATE TABLE " + targetKs + ".targettbb LIKE " + sourceKs + "." + sourceTb + " WITH INDEXES");
assertWarningsContain(res.getExecutionInfo().getWarnings(),
"Source table " + sourceKs + "." + sourceTb + " to copy indexes from to " + targetKs + ".targettbb has custom indexes. These indexes were not copied: " + customIndexes);
}
private void assertTableMetaEqualsWithoutKs(String sourceKs, String targetKs, String sourceTb, String targetTb)
{
assertTableMetaEqualsWithoutKs(sourceKs, targetKs, sourceTb, targetTb, true, true, false);
}
private void assertTableMetaEqualsWithoutKs(String sourceKs, String targetKs, String sourceTb, String targetTb, boolean compareParams, boolean compareIndexes, boolean compareIndexWithOutName)
{
TableMetadata left = getTableMetadata(sourceKs, sourceTb);
TableMetadata right = getTableMetadata(targetKs, targetTb);
assertNotNull(left);
assertNotNull(right);
assertTrue(equalsWithoutTableNameAndDropCns(left, right, compareParams, compareIndexes, compareTrigger));
assertTrue(equalsWithoutTableNameAndDropCns(left, right, compareParams, compareIndexes, compareIndexWithOutName));
assertNotEquals(left.id, right.id);
assertNotEquals(left.name, right.name);
}