Merge branch 'cassandra-5.0' into trunk

* cassandra-5.0:
  Prevent too long table names not fitting file names
This commit is contained in:
Dmitry Konstantinov 2025-09-10 23:37:50 +01:00
commit 06440e947a
13 changed files with 223 additions and 105 deletions

View File

@ -308,6 +308,7 @@ Merged from 4.1:
* Enforce CQL message size limit on multiframe messages (CASSANDRA-20052)
* Fix race condition in DecayingEstimatedHistogramReservoir during rescale (CASSANDRA-19365)
Merged from 4.0:
* Prevent too long table names not fitting file names (CASSANDRA-20389)
* Update Jackson to 2.19.2 (CASSANDRA-20848)
* Update commons-lang3 to 3.18.0 (CASSANDRA-20849)
* Add NativeTransportMaxConcurrentConnectionsPerIp to StorageProxyMBean (CASSANDRA-20642)

View File

@ -46,6 +46,8 @@ import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.messages.ResultMessage;
import static org.apache.cassandra.schema.KeyspaceMetadata.validateKeyspaceName;
abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspaceCqlStatement, SchemaTransformation
{
private static final Logger logger = LoggerFactory.getLogger(AlterSchemaStatement.class);
@ -168,7 +170,8 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
if (null != keyspace && keyspace.isVirtual())
throw ire("Virtual keyspace '%s' is not user-modifiable", keyspaceName);
validateKeyspaceName();
validateKeyspaceName(keyspaceName, AlterSchemaStatement::ire);
setExecutionTimestamp(state.getTimestamp());
// Perform a 'dry-run' attempt to apply the transformation locally before submitting to the CMS. This can save a
// round trip to the CMS for things syntax errors, but also fail fast for things like configuration errors.
@ -211,16 +214,6 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
return Schema.instance.submit(this);
}
private void validateKeyspaceName()
{
if (!SchemaConstants.isValidName(keyspaceName))
{
throw ire("Keyspace name must not be empty, more than %d characters long, " +
"or contain non-alphanumeric-underscore characters (got '%s')",
SchemaConstants.NAME_LENGTH, keyspaceName);
}
}
protected void validateDefaultTimeToLive(TableParams params)
{
if (params.defaultTimeToLive == 0

View File

@ -60,8 +60,8 @@ public final class CreateIndexStatement extends AlterSchemaStatement
public static final String CUSTOM_MULTIPLE_COLUMNS = "Only CUSTOM indexes support multiple columns";
public static final String DUPLICATE_TARGET_COLUMN = "Duplicate column '%s' in index target list";
public static final String COLUMN_DOES_NOT_EXIST = "Column '%s' doesn't exist";
public static final String INVALID_CUSTOM_INDEX_TARGET = "Column '%s' is longer than the permissible name length of %d characters or" +
" contains non-alphanumeric-underscore characters";
public static final String INVALID_CHARS_CUSTOM_INDEX_TARGET = "Column '%s' contains non-alphanumeric-underscore characters";
public static final String TOO_LONG_CUSTOM_INDEX_TARGET = "Column '%s' is longer than the permissible name length of %d characters";
public static final String COLLECTIONS_WITH_DURATIONS_NOT_SUPPORTED = "Secondary indexes are not supported on collections containing durations";
public static final String TUPLES_WITH_DURATIONS_NOT_SUPPORTED = "Secondary indexes are not supported on tuples containing durations";
public static final String DURATIONS_NOT_SUPPORTED = "Secondary indexes are not supported on duration columns";
@ -220,6 +220,14 @@ public final class CreateIndexStatement extends AlterSchemaStatement
return ImmutableSet.of();
}
private void validateCustomIndexColumnName(String name)
{
if (!SchemaConstants.isValidCharsName(name))
throw ire(INVALID_CHARS_CUSTOM_INDEX_TARGET, name);
if (name.length() > SchemaConstants.NAME_LENGTH)
throw ire(TOO_LONG_CUSTOM_INDEX_TARGET, name, SchemaConstants.NAME_LENGTH);
}
private void validateIndexTarget(TableMetadata table, IndexMetadata.Kind kind, IndexTarget target)
{
ColumnMetadata column = table.getColumn(target.column);
@ -229,8 +237,9 @@ public final class CreateIndexStatement extends AlterSchemaStatement
AbstractType<?> baseType = column.type.unwrap();
if ((kind == IndexMetadata.Kind.CUSTOM) && !SchemaConstants.isValidName(target.column.toString()))
throw ire(INVALID_CUSTOM_INDEX_TARGET, target.column, SchemaConstants.NAME_LENGTH);
// TODO: this check needs to be removed with CASSANDRA-20235
if ((kind == IndexMetadata.Kind.CUSTOM))
validateCustomIndexColumnName(target.column.toString());
if (column.type.referencesDuration())
{

View File

@ -117,6 +117,7 @@ public class Directories
public static final String SNAPSHOT_SUBDIR = "snapshots";
public static final String TMP_SUBDIR = "tmp";
public static final String SECONDARY_INDEX_NAME_SEPARATOR = ".";
public static final String TABLE_DIRECTORY_NAME_SEPARATOR = "-";
/**
* The directories used to store keyspaces data.
@ -224,14 +225,11 @@ public class Directories
this.metadata = metadata;
this.paths = paths;
ImmutableMap.Builder<Path, DataDirectory> canonicalPathsBuilder = ImmutableMap.builder();
String tableId = metadata.id.toHexString();
int idx = metadata.name.indexOf(SECONDARY_INDEX_NAME_SEPARATOR);
String cfName = idx >= 0 ? metadata.name.substring(0, idx) : metadata.name;
String indexNameWithDot = idx >= 0 ? metadata.name.substring(idx) : null;
String indexNameWithDot = metadata.getIndexNameWithDot();
this.dataPaths = new File[paths.length];
// If upgraded from version less than 2.1, use existing directories
String oldSSTableRelativePath = join(metadata.keyspace, cfName);
String oldSSTableRelativePath = join(metadata.keyspace, metadata.getTableName());
for (int i = 0; i < paths.length; ++i)
{
// check if old SSTable directory exists
@ -244,7 +242,7 @@ public class Directories
{
canonicalPathsBuilder = ImmutableMap.builder();
// use 2.1+ style
String newSSTableRelativePath = join(metadata.keyspace, cfName + '-' + tableId);
String newSSTableRelativePath = join(metadata.keyspace, metadata.getTableDirectoryName());
for (int i = 0; i < paths.length; ++i)
{
File dataPath = new File(paths[i].location, newSSTableRelativePath);

View File

@ -28,7 +28,6 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.base.Objects;
@ -56,6 +55,8 @@ import org.apache.cassandra.utils.UUIDSerializer;
import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.utils.LocalizeString.toLowerCaseLocalized;
import static org.apache.cassandra.schema.SchemaConstants.PATTERN_NON_WORD_CHAR;
import static org.apache.cassandra.schema.SchemaConstants.isValidCharsName;
/**
* An immutable representation of secondary index metadata.
@ -64,10 +65,6 @@ public final class IndexMetadata
{
private static final Logger logger = LoggerFactory.getLogger(IndexMetadata.class);
private static final Pattern PATTERN_NON_WORD_CHAR = Pattern.compile("\\W");
private static final Pattern PATTERN_WORD_CHARS = Pattern.compile("\\w+");
public static final Serializer serializer = new Serializer();
public static final MetadataSerializer metadataSerializer = new MetadataSerializer();
@ -117,29 +114,25 @@ public final class IndexMetadata
{
Map<String, String> newOptions = new HashMap<>(options);
newOptions.put(IndexTarget.TARGET_OPTION_NAME, targets.stream()
.map(target -> target.asCqlString())
.map(IndexTarget::asCqlString)
.collect(Collectors.joining(", ")));
return new IndexMetadata(name, newOptions, kind);
}
public static boolean isNameValid(String name)
{
return name != null && !name.isEmpty() && PATTERN_WORD_CHARS.matcher(name).matches();
}
public static String generateDefaultIndexName(String table, ColumnIdentifier column)
{
return PATTERN_NON_WORD_CHAR.matcher(table + "_" + column.toString() + "_idx").replaceAll("");
return PATTERN_NON_WORD_CHAR.matcher(table + '_' + column.toString() + "_idx").replaceAll("");
}
public static String generateDefaultIndexName(String table)
{
return PATTERN_NON_WORD_CHAR.matcher(table + "_" + "idx").replaceAll("");
return PATTERN_NON_WORD_CHAR.matcher(table + "_idx").replaceAll("");
}
public void validate(TableMetadata table)
{
if (!isNameValid(name))
// TODO: address validating the length by CASSANDRA-20445
if (!isValidCharsName(name))
throw new ConfigurationException("Illegal index name " + name);
if (kind == null)

View File

@ -39,6 +39,7 @@ import org.apache.cassandra.cql3.functions.UDAggregate;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
@ -53,6 +54,7 @@ import org.apache.cassandra.schema.Views.ViewsDiff;
import static com.google.common.collect.Iterables.any;
import static java.lang.String.format;
import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.schema.SchemaConstants.TABLE_NAME_LENGTH;
import static org.apache.cassandra.utils.LocalizeString.toLowerCaseLocalized;
/**
@ -62,6 +64,23 @@ public final class KeyspaceMetadata implements SchemaElement
{
public static final Serializer serializer = new Serializer();
/**
* Validates the keyspace name for valid characters and correct length.
* Throws an exception if it's invalid.
*
* @param keyspaceName The name of the keyspace to validate
* @param exceptionBuilder The exception constructor to throw if validation fails
*/
public static <T extends RequestValidationException> void validateKeyspaceName(String keyspaceName, java.util.function.Function<String, T> exceptionBuilder)
{
if (!SchemaConstants.isValidCharsName(keyspaceName))
throw exceptionBuilder.apply(format("Keyspace name must not be empty and must contain alphanumeric or underscore characters only (got \"%s\")",
keyspaceName));
if (keyspaceName.length() > SchemaConstants.NAME_LENGTH)
throw exceptionBuilder.apply(format("Keyspace name must not be more than %d characters long (got %d characters for \"%s\")",
TABLE_NAME_LENGTH, keyspaceName.length(), keyspaceName));
}
public enum Kind
{
REGULAR, VIRTUAL
@ -214,7 +233,7 @@ public final class KeyspaceMetadata implements SchemaElement
}
/**
* find an avaiable index name based on the indexes in target keyspace and indexes collections
* find an available 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
@ -381,14 +400,7 @@ public final class KeyspaceMetadata implements SchemaElement
public void validate(ClusterMetadata metadata)
{
if (!SchemaConstants.isValidName(name))
{
throw new ConfigurationException(format("Keyspace name must not be empty, more than %s characters long, "
+ "or contain non-alphanumeric-underscore characters (got \"%s\")",
SchemaConstants.NAME_LENGTH,
name));
}
validateKeyspaceName(name, ConfigurationException::new);
params.validate(name, null, metadata);
tablesAndViews().forEach(TableMetadata::validate);

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.service.accord.AccordKeyspace;
import org.apache.cassandra.tracing.TraceKeyspace;
import static org.apache.cassandra.db.Directories.TABLE_DIRECTORY_NAME_SEPARATOR;
import static org.apache.cassandra.utils.LocalizeString.toLowerCaseLocalized;
/**
@ -41,6 +42,7 @@ import static org.apache.cassandra.utils.LocalizeString.toLowerCaseLocalized;
public final class SchemaConstants
{
public static final Pattern PATTERN_WORD_CHARS = Pattern.compile("\\w+");
public static final Pattern PATTERN_NON_WORD_CHAR = Pattern.compile("\\W");
public static final String SYSTEM_KEYSPACE_NAME = "system";
public static final String SCHEMA_KEYSPACE_NAME = "system_schema";
@ -77,14 +79,37 @@ public final class SchemaConstants
*/
public static final int NAME_LENGTH = 48;
/**
* Longest acceptable file name. Longer names lead to too long file name error.
*/
public static final int FILENAME_LENGTH = 255;
/**
* Length of a table uuid as a hex string.
*/
public static final int TABLE_UUID_AS_HEX_LENGTH = 32;
/**
* Longest acceptable table name, so it can be used in a directory
* name constructed with a suffix of a table id and a separator.
*/
public static final int TABLE_NAME_LENGTH = FILENAME_LENGTH - TABLE_UUID_AS_HEX_LENGTH - TABLE_DIRECTORY_NAME_SEPARATOR.length();
// 59adb24e-f3cd-3e02-97f0-5b395827453f
public static final UUID emptyVersion;
public static final List<String> LEGACY_AUTH_TABLES = Arrays.asList("credentials", "users", "permissions");
public static boolean isValidName(String name)
/**
* Validates that a name is not empty and contains only alphanumeric characters or
* underscore, so it can be used in file or directory names.
*
* @param name the name to check
* @return whether the non-empty name contains only valid characters
*/
public static boolean isValidCharsName(String name)
{
return name != null && !name.isEmpty() && name.length() <= NAME_LENGTH && PATTERN_WORD_CHARS.matcher(name).matches();
return name != null && !name.isEmpty() && PATTERN_WORD_CHARS.matcher(name).matches();
}
static

View File

@ -59,7 +59,6 @@ import org.apache.cassandra.cql3.functions.masking.ColumnMask;
import org.apache.cassandra.db.Clustering;
import org.apache.cassandra.db.ClusteringComparator;
import org.apache.cassandra.db.Columns;
import org.apache.cassandra.db.Directories;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.RegularAndStaticColumns;
import org.apache.cassandra.db.marshal.AbstractType;
@ -90,9 +89,14 @@ import static com.google.common.collect.Iterables.transform;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.apache.cassandra.db.Directories.SECONDARY_INDEX_NAME_SEPARATOR;
import static org.apache.cassandra.db.Directories.TABLE_DIRECTORY_NAME_SEPARATOR;
import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.schema.ColumnMetadata.NO_UNIQUE_ID;
import static org.apache.cassandra.schema.IndexMetadata.isNameValid;
import static org.apache.cassandra.schema.KeyspaceMetadata.validateKeyspaceName;
import static org.apache.cassandra.schema.SchemaConstants.FILENAME_LENGTH;
import static org.apache.cassandra.schema.SchemaConstants.TABLE_NAME_LENGTH;
import static org.apache.cassandra.schema.SchemaConstants.isValidCharsName;
@Unmetered
public class TableMetadata implements SchemaElement
@ -602,11 +606,9 @@ public class TableMetadata implements SchemaElement
public void validate()
{
if (!isNameValid(keyspace))
except("Keyspace name must not be empty, more than %s characters long, or contain non-alphanumeric-underscore characters (got \"%s\")", SchemaConstants.NAME_LENGTH, keyspace);
validateKeyspaceName(keyspace, this::prepareConfigurationException);
if (!isNameValid(name))
except("Table name must not be empty, more than %s characters long, or contain non-alphanumeric-underscore characters (got \"%s\")", SchemaConstants.NAME_LENGTH, name);
validateTableName();
params.validate();
@ -649,6 +651,17 @@ public class TableMetadata implements SchemaElement
require((params.transactionalMode == TransactionalMode.off && params.transactionalMigrationFrom == TransactionalMigrationFromMode.none) || !isCounter(), "Counters are not supported with Accord for table " + this);
}
private void validateTableName()
{
if (!isValidCharsName(name))
except("Table name must not be empty or not contain non-alphanumeric-underscore characters (got \"%s\")", name);
if (name.length() > TABLE_NAME_LENGTH)
except("Table name must not be more than %d characters long (got %d characters for \"%s\")", TABLE_NAME_LENGTH, name.length(), name);
assert getTableDirectoryName().length() <= FILENAME_LENGTH : String.format("Generated directory name for a table of %d characters doesn't fit the max filename legnth of %s. This unexpectedly wasn't prevented by check of the table name length, %d, to fit %d characters (got table name \"%s\" and generated directory name \"%s\"", getTableDirectoryName().length(), FILENAME_LENGTH, name.length(), TABLE_NAME_LENGTH, name, getTableDirectoryName());
}
/**
* To support backward compatibility with thrift super columns in the C* 3.0+ storage engine, we encode said super
* columns as a CQL {@code map<blob, blob>}. To ensure the name of this map did not conflict with any other user
@ -745,7 +758,41 @@ public class TableMetadata implements SchemaElement
public String indexTableName(IndexMetadata info)
{
// TODO simplify this when info.index_name is guaranteed to be set
return name + Directories.SECONDARY_INDEX_NAME_SEPARATOR + info.name;
return name + SECONDARY_INDEX_NAME_SEPARATOR + info.name;
}
/**
* Returns the table part of the index table name or the entire table name
* if not an index table.
* @return table name part
*/
public String getTableName()
{
int idx = name.indexOf(SECONDARY_INDEX_NAME_SEPARATOR);
return idx >= 0 ? name.substring(0, idx) : name;
}
/**
* Generates a directory name for the table by using table part of
* the (index) table name and table id.
* @return directory name
*/
public String getTableDirectoryName()
{
return getTableName() + TABLE_DIRECTORY_NAME_SEPARATOR + id.toHexString();
}
/**
* Returns the index name from the name of an index table
* including the dot prefixing the index name, see {@link #indexTableName}.
* If not an index table, returns null.
* @return index name prefixed with dot prefix or null
*/
@Nullable
public String getIndexNameWithDot()
{
int idx = name.indexOf(SECONDARY_INDEX_NAME_SEPARATOR);
return idx >= 0 ? name.substring(idx) : null;
}
/**
@ -811,9 +858,14 @@ public class TableMetadata implements SchemaElement
return builder.build();
}
private ConfigurationException prepareConfigurationException(String format, Object... args)
{
return new ConfigurationException(keyspace + '.' + name + ": " + format(format, args));
}
protected void except(String format, Object... args)
{
throw new ConfigurationException(keyspace + "." + name + ": " + format(format, args));
throw prepareConfigurationException(format, args);
}
@Override
@ -1799,7 +1851,7 @@ public class TableMetadata implements SchemaElement
literals[i++] = asCQLLiteral(clusteringColumns().get(j).type, clustering.bufferAt(j));
}
return i == 1 ? literals[0] : "(" + String.join(", ", literals) + ")";
return i == 1 ? literals[0] : '(' + String.join(", ", literals) + ')';
}
private static String asCQLLiteral(AbstractType<?> type, ByteBuffer value)

View File

@ -74,7 +74,6 @@ import org.apache.cassandra.inject.Injection;
import org.apache.cassandra.inject.Injections;
import org.apache.cassandra.inject.InvokePointBuilder;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.Throwables;
@ -263,7 +262,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
assertThatThrownBy(() -> executeNet(String.format("CREATE INDEX ON %%s(\"%s\")" +
" USING 'sai'", invalidColumn)))
.isInstanceOf(InvalidQueryException.class)
.hasMessage(String.format(CreateIndexStatement.INVALID_CUSTOM_INDEX_TARGET, invalidColumn, SchemaConstants.NAME_LENGTH));
.hasMessage(String.format(CreateIndexStatement.INVALID_CHARS_CUSTOM_INDEX_TARGET, invalidColumn));
}
@Test

View File

@ -26,6 +26,10 @@ import org.apache.cassandra.utils.BloomCalculations;
import org.junit.Test;
import static org.apache.cassandra.schema.SchemaConstants.FILENAME_LENGTH;
import static org.apache.cassandra.schema.SchemaConstants.NAME_LENGTH;
import static org.apache.cassandra.schema.SchemaConstants.TABLE_NAME_LENGTH;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
public class CreateTableValidationTest extends CQLTester
@ -91,6 +95,42 @@ public class CreateTableValidationTest extends CQLTester
"Missing CLUSTERING ORDER for column ck1");
}
@Test
public void testCreatingTableWithLongName() throws Throwable
{
int tableIdSuffix = "-1b255f4def2540a60000000000000000".length();
String keyspaceName = "k".repeat(NAME_LENGTH);
String tableName = "t".repeat(FILENAME_LENGTH - tableIdSuffix);
String tooLongTableName = "l".repeat(FILENAME_LENGTH - tableIdSuffix + 1);
execute(String.format("CREATE KEYSPACE %s with replication = " +
"{ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
keyspaceName));
assertInvalidMessage(String.format("%s.%s: Table name must not be more than %d characters long (got %d characters for \"%2$s\")",
keyspaceName, tooLongTableName, TABLE_NAME_LENGTH, tooLongTableName.length()),
String.format("CREATE TABLE %s.%s (" +
"key int PRIMARY KEY," +
"val int)", keyspaceName, tooLongTableName));
createTable(String.format("CREATE TABLE %s.%s (" +
"key int PRIMARY KEY," +
"val int)", keyspaceName, tableName));
execute(String.format("INSERT INTO %s.%s (key,val) VALUES (1,1)", keyspaceName, tableName));
assertThat(execute(String.format("SELECT * from %s.%s", keyspaceName, tableName))).hasSize(1);
flush(keyspaceName, tableName);
assertThat(execute(String.format("SELECT * from %s.%s", keyspaceName, tableName))).hasSize(1);
}
@Test
public void testNonAlphanummericTableName() throws Throwable
{
assertInvalidMessage(String.format("%s.d-3: Table name must not be empty or not contain non-alphanumeric-underscore characters (got \"d-3\")", KEYSPACE),
String.format("CREATE TABLE %s.\"d-3\" (key int PRIMARY KEY, val int)", KEYSPACE));
assertInvalidMessage(String.format("%s. : Table name must not be empty or not contain non-alphanumeric-underscore characters (got \" \")", KEYSPACE),
String.format("CREATE TABLE %s.\" \" (key int PRIMARY KEY, val int)", KEYSPACE));
}
private void expectedFailure(final Class<? extends RequestValidationException> exceptionType, String statement, String errorMsg)
{

View File

@ -25,29 +25,8 @@ import org.junit.Test;
import org.apache.cassandra.cql3.ColumnIdentifier;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class IndexMetadataTest {
@Test
public void testIsNameValidPositive()
{
assertTrue(IndexMetadata.isNameValid("abcdefghijklmnopqrstuvwxyz"));
assertTrue(IndexMetadata.isNameValid("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
assertTrue(IndexMetadata.isNameValid("_01234567890"));
}
@Test
public void testIsNameValidNegative()
{
assertFalse(IndexMetadata.isNameValid(null));
assertFalse(IndexMetadata.isNameValid(""));
assertFalse(IndexMetadata.isNameValid(" "));
assertFalse(IndexMetadata.isNameValid("@"));
assertFalse(IndexMetadata.isNameValid("!"));
}
public class IndexMetadataTest
{
@Test
public void testGetDefaultIndexName()
{

View File

@ -24,6 +24,8 @@ import java.util.Map;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableMap;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@ -52,8 +54,11 @@ import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.Util.throwAssert;
import static org.apache.cassandra.cql3.CQLTester.assertRows;
import static org.apache.cassandra.cql3.CQLTester.row;
import static org.apache.cassandra.schema.SchemaConstants.NAME_LENGTH;
import static org.apache.cassandra.utils.AssertionUtils.assertThatThrownBy;
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.assertNull;
import static org.junit.Assert.assertTrue;
@ -131,13 +136,21 @@ public class SchemaChangesTest
@Test
public void testInvalidNames()
{
String[] valid = {"1", "a", "_1", "b_", "__", "1_a"};
String[] valid = { "1", "a", "_1", "b_", "__", "1_a" };
for (String s : valid)
assertTrue(SchemaConstants.isValidName(s));
String[] invalid = {"b@t", "dash-y", "", " ", "dot.s", ".hidden"};
{
assertTrue(SchemaConstants.isValidCharsName(s));
KeyspaceMetadata.validateKeyspaceName(s, InvalidRequestException::new);
}
String[] invalid = { "b@t", "dash-y", "", " ", "dot.s", ".hidden" };
for (String s : invalid)
assertFalse(SchemaConstants.isValidName(s));
{
assertFalse(SchemaConstants.isValidCharsName(s));
assertThatThrownBy(() -> KeyspaceMetadata.validateKeyspaceName(s, InvalidRequestException::new)).isInstanceOf(InvalidRequestException.class);
}
KeyspaceMetadata.validateKeyspaceName("k".repeat(NAME_LENGTH), InvalidRequestException::new);
assertThatThrownBy(() -> KeyspaceMetadata.validateKeyspaceName("k".repeat(NAME_LENGTH + 1), InvalidRequestException::new)).isInstanceOf(InvalidRequestException.class);
}
@Test
@ -341,7 +354,7 @@ public class SchemaChangesTest
KeyspaceMetadata newFetchedKs = Schema.instance.getKeyspaceMetadata(newKs.name);
assertEquals(newFetchedKs.params.replication.klass, newKs.params.replication.klass);
assertFalse(newFetchedKs.params.replication.klass.equals(oldKs.params.replication.klass));
assertNotEquals(newFetchedKs.params.replication.klass, oldKs.params.replication.klass);
}
/*
@ -478,40 +491,44 @@ public class SchemaChangesTest
}
@Test
public void testValidateNullKeyspace()
public void testTableMetadataBuilderWithInvalidKeyspace()
{
TableMetadata.Builder builder = TableMetadata.builder(null, TABLE1).addPartitionKeyColumn("partitionKey", BytesType.instance);
try
String[][] keyspaces =
{
{ null, "Keyspace name must not be empty" },
{ "a@b", "Keyspace name must not be empty and must contain alphanumeric or underscore characters only" },
{ "k".repeat(NAME_LENGTH), String.format("Keyspace name must not be more than %d characters long", NAME_LENGTH) }
};
for (String[] keyspace : keyspaces)
{
TableMetadata.Builder builder = TableMetadata.builder(keyspace[0], TABLE1).addPartitionKeyColumn("partitionKey", BytesType.instance);
thrown.expect(ConfigurationException.class);
thrown.expectMessage(String.format("%s.%s: %s", keyspace[0], TABLE1, keyspace[1]));
builder.build();
}
catch (ConfigurationException e)
{
assertTrue(e.getMessage().contains(null + "." + TABLE1 + ": Keyspace name must not be empty"));
}
}
@Test
public void testValidateCompatibilityIDMismatch() throws Exception
public void testValidateCompatibilityIDMismatch()
{
TableMetadata.Builder builder = TableMetadata.builder(KEYSPACE1, TABLE1).addPartitionKeyColumn("partitionKey", BytesType.instance);
TableMetadata table1 = builder.build();
TableMetadata table2 = table1.unbuild().id(TableId.generate()).build();
thrown.expect(ConfigurationException.class);
thrown.expectMessage(KEYSPACE1 + "." + TABLE1 + ": Table ID mismatch");
thrown.expectMessage(KEYSPACE1 + '.' + TABLE1 + ": Table ID mismatch");
table1.validateCompatibility(table2);
}
@Test
public void testValidateCompatibilityNameMismatch() throws Exception
public void testValidateCompatibilityNameMismatch()
{
TableMetadata.Builder builder1 = TableMetadata.builder(KEYSPACE1, TABLE1).addPartitionKeyColumn("partitionKey", BytesType.instance);
TableMetadata.Builder builder2 = TableMetadata.builder(KEYSPACE1, TABLE2).addPartitionKeyColumn("partitionKey", BytesType.instance);
TableMetadata table1 = builder1.build();
TableMetadata table2 = builder2.build();
thrown.expect(ConfigurationException.class);
thrown.expectMessage(KEYSPACE1 + "." + TABLE1 + ": Table mismatch");
thrown.expectMessage(KEYSPACE1 + '.' + TABLE1 + ": Table mismatch");
table1.validateCompatibility(table2);
}

View File

@ -50,19 +50,19 @@ public class ValidationTest extends CassandraTestBase
@Test
public void testIsNameValidPositive()
{
assertTrue(SchemaConstants.isValidName("abcdefghijklmnopqrstuvwxyz"));
assertTrue(SchemaConstants.isValidName("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
assertTrue(SchemaConstants.isValidName("_01234567890"));
assertTrue(SchemaConstants.isValidCharsName("abcdefghijklmnopqrstuvwxyz"));
assertTrue(SchemaConstants.isValidCharsName("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
assertTrue(SchemaConstants.isValidCharsName("_01234567890"));
}
@Test
public void testIsNameValidNegative()
{
assertFalse(SchemaConstants.isValidName(null));
assertFalse(SchemaConstants.isValidName(""));
assertFalse(SchemaConstants.isValidName(" "));
assertFalse(SchemaConstants.isValidName("@"));
assertFalse(SchemaConstants.isValidName("!"));
assertFalse(SchemaConstants.isValidCharsName(null));
assertFalse(SchemaConstants.isValidCharsName(""));
assertFalse(SchemaConstants.isValidCharsName(" "));
assertFalse(SchemaConstants.isValidCharsName("@"));
assertFalse(SchemaConstants.isValidCharsName("!"));
}
private static final Set<String> primitiveTypes =