Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/3079ae60 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/3079ae60 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/3079ae60 Branch: refs/heads/trunk Commit: 3079ae60d29baec262a4b05d7082e88091299d26 Parents: 8bfe09f e5c4027 Author: Aleksey Yeschenko <alek...@apache.org> Authored: Wed Apr 27 17:55:27 2016 +0100 Committer: Aleksey Yeschenko <alek...@apache.org> Committed: Wed Apr 27 17:57:59 2016 +0100 ---------------------------------------------------------------------- CHANGES.txt | 3 ++- .../cql3/statements/AlterTableStatement.java | 2 +- .../cassandra/cql3/statements/AlterTypeStatement.java | 2 +- .../cql3/statements/CreateIndexStatement.java | 2 +- .../cql3/statements/CreateTriggerStatement.java | 2 +- .../cassandra/cql3/statements/DropIndexStatement.java | 2 +- .../cql3/statements/DropTriggerStatement.java | 2 +- .../org/apache/cassandra/schema/SchemaKeyspace.java | 14 ++------------ .../apache/cassandra/service/MigrationManager.java | 8 ++++---- .../org/apache/cassandra/thrift/CassandraServer.java | 2 +- test/unit/org/apache/cassandra/schema/DefsTest.java | 14 +++++++------- .../apache/cassandra/schema/SchemaKeyspaceTest.java | 2 +- .../apache/cassandra/triggers/TriggersSchemaTest.java | 4 ++-- 13 files changed, 25 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index bc15d32,3641816..6b6bc1f --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,27 -1,11 +1,28 @@@ -2.2.7 +3.0.6 + * Fix sstabledump not showing cells after tombstone marker (CASSANDRA-11654) + * Ignore all LocalStrategy keyspaces for streaming and other related + operations (CASSANDRA-11627) + * Ensure columnfilter covers indexed columns for thrift 2i queries (CASSANDRA-11523) + * Only open one sstable scanner per sstable (CASSANDRA-11412) + * Option to specify ProtocolVersion in cassandra-stress (CASSANDRA-11410) + * ArithmeticException in avgFunctionForDecimal (CASSANDRA-11485) + * LogAwareFileLister should only use OLD sstable files in current folder to determine disk consistency (CASSANDRA-11470) + * Notify indexers of expired rows during compaction (CASSANDRA-11329) + * Properly respond with ProtocolError when a v1/v2 native protocol + header is received (CASSANDRA-11464) + * Validate that num_tokens and initial_token are consistent with one another (CASSANDRA-10120) +Merged from 2.2: + * Fix is_dense recalculation for Thrift-updated tables (CASSANDRA-11502) * Remove unnescessary file existence check during anticompaction (CASSANDRA-11660) * Add missing files to debian packages (CASSANDRA-11642) * Avoid calling Iterables::concat in loops during ModificationStatement::getFunctions (CASSANDRA-11621) * cqlsh: COPY FROM should use regular inserts for single statement batches and - report errors correctly if workers processes crash on initialization (CASSANDRA-11474) + report errors correctly if workers processes crash on initialization (CASSANDRA-11474) * Always close cluster with connection in CqlRecordWriter (CASSANDRA-11553) + * Allow only DISTINCT queries with partition keys restrictions (CASSANDRA-11339) + * CqlConfigHelper no longer requires both a keystore and truststore to work (CASSANDRA-11532) + * Make deprecated repair methods backward-compatible with previous notification service (CASSANDRA-11430) + * IncomingStreamingConnection version check message wrong (CASSANDRA-11462) Merged from 2.1: * cqlsh COPY FROM fails for null values with non-prepared statements (CASSANDRA-11631) * Make cython optional in pylib/setup.py (CASSANDRA-11630) http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java index 3515c6b,f4a7b39..381971f --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@@ -322,61 -284,8 +322,61 @@@ public class AlterTableStatement extend break; } - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); - return true; + + if (viewUpdates != null) + { + for (ViewDefinition viewUpdate : viewUpdates) + MigrationManager.announceViewUpdate(viewUpdate, isLocalOnly); + } + return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); + } + + private static void validateAlter(CFMetaData cfm, ColumnDefinition def, AbstractType<?> validatorType) + { + switch (def.kind) + { + case PARTITION_KEY: + if (validatorType instanceof CounterColumnType) + throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", def.name)); + + AbstractType<?> currentType = cfm.getKeyValidatorAsClusteringComparator().subtype(def.position()); + if (!validatorType.isValueCompatibleWith(currentType)) + throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.", + def.name, + currentType.asCQL3Type(), + validatorType.asCQL3Type())); + break; + case CLUSTERING: + if (!cfm.isCQLTable()) + throw new InvalidRequestException(String.format("Cannot alter clustering column %s in a non-CQL3 table", def.name)); + + AbstractType<?> oldType = cfm.comparator.subtype(def.position()); + // Note that CFMetaData.validateCompatibility already validate the change we're about to do. However, the error message it + // sends is a bit cryptic for a CQL3 user, so validating here for a sake of returning a better error message + // Do note that we need isCompatibleWith here, not just isValueCompatibleWith. + if (!validatorType.isCompatibleWith(oldType)) + { + throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are not order-compatible.", + def.name, + oldType.asCQL3Type(), + validatorType.asCQL3Type())); + } + break; + case REGULAR: + case STATIC: + // Thrift allows to change a column validator so CFMetaData.validateCompatibility will let it slide + // if we change to an incompatible type (contrarily to the comparator case). But we don't want to + // allow it for CQL3 (see #5882) so validating it explicitly here. We only care about value compatibility + // though since we won't compare values (except when there is an index, but that is validated by + // ColumnDefinition already). + if (!validatorType.isValueCompatibleWith(def.type)) + throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.", + def.name, + def.type.asCQL3Type(), + validatorType.asCQL3Type())); + break; + } } public String toString() http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java index 8e51c26,9203cf9..bd23971 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java @@@ -106,21 -113,11 +106,21 @@@ public abstract class AlterTypeStatemen for (ColumnDefinition def : copy.allColumns()) modified |= updateDefinition(copy, def, toUpdate.keyspace, toUpdate.name, updated); if (modified) - MigrationManager.announceColumnFamilyUpdate(copy, false, isLocalOnly); + MigrationManager.announceColumnFamilyUpdate(copy, isLocalOnly); } + for (ViewDefinition view : ksm.views) + { + ViewDefinition copy = view.copy(); + boolean modified = false; + for (ColumnDefinition def : copy.metadata.allColumns()) + modified |= updateDefinition(copy.metadata, def, toUpdate.keyspace, toUpdate.name, updated); + if (modified) + MigrationManager.announceViewUpdate(copy, isLocalOnly); + } + // Other user types potentially using the updated type - for (UserType ut : ksm.userTypes.getAllTypes().values()) + for (UserType ut : ksm.types) { // Re-updating the type we've just updated would be harmless but useless so we avoid it. // Besides, we use the occasion to drop the old version of the type if it's a type rename http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java index df1965a,d93c0a7..2eebe0d --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java @@@ -220,29 -186,17 +220,29 @@@ public class CreateIndexStatement exten } else { - cd.setIndexType(IndexType.KEYS, Collections.<String, String>emptyMap()); + indexOptions = Collections.emptyMap(); + kind = cfm.isCompound() ? IndexMetadata.Kind.COMPOSITES : IndexMetadata.Kind.KEYS; } - cd.setIndexName(indexName); - cfm.addDefaultIndexNames(); + IndexMetadata index = IndexMetadata.fromIndexTargets(cfm, targets, acceptedName, kind, indexOptions); + + // check to disallow creation of an index which duplicates an existing one in all but name + Optional<IndexMetadata> existingIndex = Iterables.tryFind(cfm.getIndexes(), existing -> existing.equalsWithoutName(index)); + if (existingIndex.isPresent()) + { + if (ifNotExists) + return null; + else + throw new InvalidRequestException(String.format("Index %s is a duplicate of existing index %s", + index.name, + existingIndex.get().name)); + } + + logger.trace("Updating index definition for {}", indexName); + cfm.indexes(cfm.getIndexes().with(index)); + - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); - return true; - } - public Event.SchemaChange changeEvent() - { // Creating an index is akin to updating the CF return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java index 2720749,ef2f263..94cfc15 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java @@@ -72,22 -68,24 +72,22 @@@ public class CreateTriggerStatement ext } } - public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException + public Event.SchemaChange announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException { CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).copy(); + Triggers triggers = cfm.getTriggers(); - TriggerDefinition triggerDefinition = TriggerDefinition.create(triggerName, triggerClass); - - if (!ifNotExists || !cfm.containsTriggerDefinition(triggerDefinition)) + if (triggers.get(triggerName).isPresent()) { - cfm.addTriggerDefinition(triggerDefinition); - logger.info("Adding trigger with name {} and class {}", triggerName, triggerClass); - MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); - return true; + if (ifNotExists) + return null; + else + throw new InvalidRequestException(String.format("Trigger %s already exists", triggerName)); } - return false; - } - public Event.SchemaChange changeEvent() - { + cfm.triggers(triggers.with(TriggerMetadata.create(triggerName, triggerClass))); + logger.info("Adding trigger with name {} and class {}", triggerName, triggerClass); - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); ++ MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java index eaf755f,0d33e57..85f5f0d --- a/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java @@@ -66,48 -85,58 +66,48 @@@ public class DropIndexStatement extend @Override public ResultMessage execute(QueryState state, QueryOptions options) throws RequestValidationException { - announceMigration(false); - return indexedCF == null ? null : new ResultMessage.SchemaChange(changeEvent()); + Event.SchemaChange ce = announceMigration(false); + return ce == null ? null : new ResultMessage.SchemaChange(ce); } - public boolean announceMigration(boolean isLocalOnly) throws InvalidRequestException, ConfigurationException + public Event.SchemaChange announceMigration(boolean isLocalOnly) throws InvalidRequestException, ConfigurationException { - CFMetaData cfm = findIndexedCF(); + CFMetaData cfm = lookupIndexedTable(); if (cfm == null) - return false; + return null; - CFMetaData updatedCfm = updateCFMetadata(cfm); - indexedCF = updatedCfm.cfName; + CFMetaData updatedCfm = cfm.copy(); + updatedCfm.indexes(updatedCfm.getIndexes().without(indexName)); - MigrationManager.announceColumnFamilyUpdate(updatedCfm, false, isLocalOnly); + MigrationManager.announceColumnFamilyUpdate(updatedCfm, isLocalOnly); - return true; - } - - private CFMetaData updateCFMetadata(CFMetaData cfm) - { - ColumnDefinition column = findIndexedColumn(cfm); - assert column != null; - CFMetaData cloned = cfm.copy(); - ColumnDefinition toChange = cloned.getColumnDefinition(column.name); - assert toChange.getIndexName() != null && toChange.getIndexName().equals(indexName); - toChange.setIndexName(null); - toChange.setIndexType(null, null); - return cloned; + // Dropping an index is akin to updating the CF + // Note that we shouldn't call columnFamily() at this point because the index has been dropped and the call to lookupIndexedTable() + // in that method would now throw. + return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, cfm.ksName, cfm.cfName); } - private CFMetaData findIndexedCF() throws InvalidRequestException + /** + * The table for which the index should be dropped, or null if the index doesn't exist + * + * @return the metadata for the table containing the dropped index, or {@code null} + * if the index to drop cannot be found but "IF EXISTS" is set on the statement. + * + * @throws InvalidRequestException if the index cannot be found and "IF EXISTS" is not + * set on the statement. + */ + private CFMetaData lookupIndexedTable() { - KSMetaData ksm = Schema.instance.getKSMetaData(keyspace()); + KeyspaceMetadata ksm = Schema.instance.getKSMetaData(keyspace()); if (ksm == null) throw new KeyspaceNotDefinedException("Keyspace " + keyspace() + " does not exist"); - for (CFMetaData cfm : ksm.cfMetaData().values()) - { - if (findIndexedColumn(cfm) != null) - return cfm; - } - if (ifExists) - return null; - else - throw new InvalidRequestException("Index '" + indexName + "' could not be found in any of the tables of keyspace '" + keyspace() + '\''); - } - - private ColumnDefinition findIndexedColumn(CFMetaData cfm) - { - for (ColumnDefinition column : cfm.allColumns()) - { - if (column.getIndexType() != null && column.getIndexName() != null && column.getIndexName().equals(indexName)) - return column; - } - return null; + return ksm.findIndexedTable(indexName) + .orElseGet(() -> { + if (ifExists) + return null; + else + throw new InvalidRequestException(String.format("Index '%s' could not be found in any " + + "of the tables of keyspace '%s'", + indexName, keyspace())); + }); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/3079ae60/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java index 562b4e8,8267b4e..3f61e01 --- a/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java @@@ -58,22 -57,22 +58,22 @@@ public class DropTriggerStatement exten ThriftValidation.validateColumnFamily(keyspace(), columnFamily()); } - public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException + public Event.SchemaChange announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException { CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).copy(); - if (cfm.removeTrigger(triggerName)) + Triggers triggers = cfm.getTriggers(); + + if (!triggers.get(triggerName).isPresent()) { - logger.info("Dropping trigger with name {}", triggerName); - MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); - return true; + if (ifExists) + return null; + else + throw new InvalidRequestException(String.format("Trigger %s was not found", triggerName)); } - if (!ifExists) - throw new InvalidRequestException(String.format("Trigger %s was not found", triggerName)); - return false; - } - public Event.SchemaChange changeEvent() - { + logger.info("Dropping trigger with name {}", triggerName); + cfm.triggers(triggers.without(triggerName)); - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); ++ MigrationManager.announceColumnFamilyUpdate(cfm, isLocalOnly); return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); } }