http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/Schema.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/Schema.java b/src/java/org/apache/cassandra/schema/Schema.java index 09ec62a..fc09c24 100644 --- a/src/java/org/apache/cassandra/schema/Schema.java +++ b/src/java/org/apache/cassandra/schema/Schema.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.schema; -import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -38,8 +37,13 @@ import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.UnknownTableException; +import org.apache.cassandra.gms.ApplicationState; +import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.locator.LocalStrategy; +import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff; +import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff; +import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.Pair; import org.cliffc.high_scale_lib.NonBlockingHashMap; @@ -79,20 +83,6 @@ public final class Schema } /** - * Validates that the provided keyspace is not one of the system keyspace. - * - * @param keyspace the keyspace name to validate. - * - * @throws InvalidRequestException if {@code keyspace} is the name of a - * system keyspace. - */ - public static void validateKeyspaceNotSystem(String keyspace) - { - if (SchemaConstants.isLocalSystemKeyspace(keyspace)) - throw new InvalidRequestException(format("%s keyspace is not user-modifiable", keyspace)); - } - - /** * load keyspace (keyspace) definitions, but do not initialize the keyspace instances. * Schema version may be updated as the result. */ @@ -108,22 +98,12 @@ public final class Schema */ public void loadFromDisk(boolean updateVersion) { - load(SchemaKeyspace.fetchNonSystemKeyspaces()); + SchemaKeyspace.fetchNonSystemKeyspaces().forEach(this::load); if (updateVersion) updateVersion(); } /** - * Load up non-system keyspaces - * - * @param keyspaceDefs The non-system keyspace definitions - */ - private void load(Iterable<KeyspaceMetadata> keyspaceDefs) - { - keyspaceDefs.forEach(this::load); - } - - /** * Update (or insert) new keyspace definition * * @param ksm The metadata about keyspace @@ -153,50 +133,33 @@ public final class Schema private void reload(KeyspaceMetadata previous, KeyspaceMetadata updated) { Keyspace keyspace = getKeyspaceInstance(updated.name); - if (keyspace != null) + if (null != keyspace) keyspace.setMetadata(updated); - MapDifference<TableId, TableMetadata> tablesDiff = previous.tables.diff(updated.tables); - MapDifference<TableId, ViewMetadata> viewsDiff = previous.views.diff(updated.views); + Tables.TablesDiff tablesDiff = Tables.diff(previous.tables, updated.tables); + Views.ViewsDiff viewsDiff = Views.diff(previous.views, updated.views); + MapDifference<String, TableMetadata> indexesDiff = previous.tables.indexesDiff(updated.tables); // clean up after removed entries - - tablesDiff.entriesOnlyOnLeft() - .values() - .forEach(table -> metadataRefs.remove(table.id)); - - viewsDiff.entriesOnlyOnLeft() - .values() - .forEach(view -> metadataRefs.remove(view.metadata.id)); + tablesDiff.dropped.forEach(table -> metadataRefs.remove(table.id)); + viewsDiff.dropped.forEach(view -> metadataRefs.remove(view.metadata.id)); indexesDiff.entriesOnlyOnLeft() .values() .forEach(indexTable -> indexMetadataRefs.remove(Pair.create(indexTable.keyspace, indexTable.indexName().get()))); // load up new entries - - tablesDiff.entriesOnlyOnRight() - .values() - .forEach(table -> metadataRefs.put(table.id, new TableMetadataRef(table))); - - viewsDiff.entriesOnlyOnRight() - .values() - .forEach(view -> metadataRefs.put(view.metadata.id, new TableMetadataRef(view.metadata))); + tablesDiff.created.forEach(table -> metadataRefs.put(table.id, new TableMetadataRef(table))); + viewsDiff.created.forEach(view -> metadataRefs.put(view.metadata.id, new TableMetadataRef(view.metadata))); indexesDiff.entriesOnlyOnRight() .values() .forEach(indexTable -> indexMetadataRefs.put(Pair.create(indexTable.keyspace, indexTable.indexName().get()), new TableMetadataRef(indexTable))); // refresh refs to updated ones - - tablesDiff.entriesDiffering() - .values() - .forEach(diff -> metadataRefs.get(diff.rightValue().id).set(diff.rightValue())); - - viewsDiff.entriesDiffering() - .values() - .forEach(diff -> metadataRefs.get(diff.rightValue().metadata.id).set(diff.rightValue().metadata)); + tablesDiff.altered.forEach(diff -> metadataRefs.get(diff.after.id).set(diff.after)); + viewsDiff.altered.forEach(diff -> metadataRefs.get(diff.after.metadata.id).set(diff.after.metadata)); indexesDiff.entriesDiffering() .values() @@ -556,7 +519,16 @@ public final class Schema public void updateVersionAndAnnounce() { updateVersion(); - MigrationManager.passiveAnnounce(version); + passiveAnnounceVersion(); + } + + /** + * Announce my version passively over gossip. + * Used to notify nodes as they arrive in the cluster. + */ + private void passiveAnnounceVersion() + { + Gossiper.instance.addLocalApplicationState(ApplicationState.SCHEMA, StorageService.instance.valueFactory.schema(version)); } /** @@ -576,7 +548,7 @@ public final class Schema { Keyspaces before = keyspaces.filter(k -> !SchemaConstants.isLocalSystemKeyspace(k.name)); Keyspaces after = SchemaKeyspace.fetchNonSystemKeyspaces(); - merge(before, after); + merge(Keyspaces.diff(before, after)); updateVersionAndAnnounce(); } @@ -594,6 +566,60 @@ public final class Schema updateVersionAndAnnounce(); } + public synchronized TransformationResult transform(SchemaTransformation transformation, boolean locally, long now) + { + KeyspacesDiff diff; + try + { + Keyspaces before = keyspaces; + Keyspaces after = transformation.apply(before); + diff = Keyspaces.diff(before, after); + } + catch (RuntimeException e) + { + return new TransformationResult(e); + } + + if (diff.isEmpty()) + return new TransformationResult(diff, Collections.emptyList()); + + Collection<Mutation> mutations = SchemaKeyspace.convertSchemaDiffToMutations(diff, now); + SchemaKeyspace.applyChanges(mutations); + + merge(diff); + updateVersion(); + if (!locally) + passiveAnnounceVersion(); + + return new TransformationResult(diff, mutations); + } + + public static final class TransformationResult + { + public final boolean success; + public final RuntimeException exception; + public final KeyspacesDiff diff; + public final Collection<Mutation> mutations; + + private TransformationResult(boolean success, RuntimeException exception, KeyspacesDiff diff, Collection<Mutation> mutations) + { + this.success = success; + this.exception = exception; + this.diff = diff; + this.mutations = mutations; + } + + TransformationResult(RuntimeException exception) + { + this(false, exception, null, null); + } + + TransformationResult(KeyspacesDiff diff, Collection<Mutation> mutations) + { + this(true, null, diff, mutations); + } + } + synchronized void merge(Collection<Mutation> mutations) { // only compare the keyspaces affected by this set of schema mutations @@ -608,71 +634,57 @@ public final class Schema // apply the schema mutations and fetch the new versions of the altered keyspaces Keyspaces after = SchemaKeyspace.fetchKeyspaces(affectedKeyspaces); - merge(before, after); + merge(Keyspaces.diff(before, after)); } - private synchronized void merge(Keyspaces before, Keyspaces after) + private void merge(KeyspacesDiff diff) { - MapDifference<String, KeyspaceMetadata> keyspacesDiff = before.diff(after); - - // dropped keyspaces - keyspacesDiff.entriesOnlyOnLeft().values().forEach(this::dropKeyspace); - - // new keyspaces - keyspacesDiff.entriesOnlyOnRight().values().forEach(this::createKeyspace); - - // updated keyspaces - keyspacesDiff.entriesDiffering().entrySet().forEach(diff -> alterKeyspace(diff.getValue().leftValue(), diff.getValue().rightValue())); + diff.dropped.forEach(this::dropKeyspace); + diff.created.forEach(this::createKeyspace); + diff.altered.forEach(this::alterKeyspace); } - private void alterKeyspace(KeyspaceMetadata before, KeyspaceMetadata after) + private void alterKeyspace(KeyspaceDiff delta) { - // calculate the deltas - MapDifference<TableId, TableMetadata> tablesDiff = before.tables.diff(after.tables); - MapDifference<TableId, ViewMetadata> viewsDiff = before.views.diff(after.views); - MapDifference<ByteBuffer, UserType> typesDiff = before.types.diff(after.types); - MapDifference<Pair<FunctionName, List<String>>, UDFunction> udfsDiff = before.functions.udfsDiff(after.functions); - MapDifference<Pair<FunctionName, List<String>>, UDAggregate> udasDiff = before.functions.udasDiff(after.functions); - // drop tables and views - viewsDiff.entriesOnlyOnLeft().values().forEach(this::dropView); - tablesDiff.entriesOnlyOnLeft().values().forEach(this::dropTable); + delta.views.dropped.forEach(this::dropView); + delta.tables.dropped.forEach(this::dropTable); - load(after); + load(delta.after); // add tables and views - tablesDiff.entriesOnlyOnRight().values().forEach(this::createTable); - viewsDiff.entriesOnlyOnRight().values().forEach(this::createView); + delta.tables.created.forEach(this::createTable); + delta.views.created.forEach(this::createView); // update tables and views - tablesDiff.entriesDiffering().values().forEach(diff -> alterTable(diff.rightValue())); - viewsDiff.entriesDiffering().values().forEach(diff -> alterView(diff.rightValue())); + delta.tables.altered.forEach(diff -> alterTable(diff.after)); + delta.views.altered.forEach(diff -> alterView(diff.after)); // deal with all removed, added, and altered views - Keyspace.open(before.name).viewManager.reload(true); + Keyspace.open(delta.after.name).viewManager.reload(true); // notify on everything dropped - udasDiff.entriesOnlyOnLeft().values().forEach(this::notifyDropAggregate); - udfsDiff.entriesOnlyOnLeft().values().forEach(this::notifyDropFunction); - viewsDiff.entriesOnlyOnLeft().values().forEach(this::notifyDropView); - tablesDiff.entriesOnlyOnLeft().values().forEach(this::notifyDropTable); - typesDiff.entriesOnlyOnLeft().values().forEach(this::notifyDropType); + delta.udas.dropped.forEach(uda -> notifyDropAggregate((UDAggregate) uda)); + delta.udfs.dropped.forEach(udf -> notifyDropFunction((UDFunction) udf)); + delta.views.dropped.forEach(this::notifyDropView); + delta.tables.dropped.forEach(this::notifyDropTable); + delta.types.dropped.forEach(this::notifyDropType); // notify on everything created - typesDiff.entriesOnlyOnRight().values().forEach(this::notifyCreateType); - tablesDiff.entriesOnlyOnRight().values().forEach(this::notifyCreateTable); - viewsDiff.entriesOnlyOnRight().values().forEach(this::notifyCreateView); - udfsDiff.entriesOnlyOnRight().values().forEach(this::notifyCreateFunction); - udasDiff.entriesOnlyOnRight().values().forEach(this::notifyCreateAggregate); + delta.types.created.forEach(this::notifyCreateType); + delta.tables.created.forEach(this::notifyCreateTable); + delta.views.created.forEach(this::notifyCreateView); + delta.udfs.created.forEach(udf -> notifyCreateFunction((UDFunction) udf)); + delta.udas.created.forEach(uda -> notifyCreateAggregate((UDAggregate) uda)); // notify on everything altered - if (!before.params.equals(after.params)) - notifyAlterKeyspace(after); - typesDiff.entriesDiffering().values().forEach(diff -> notifyAlterType(diff.rightValue())); - tablesDiff.entriesDiffering().values().forEach(diff -> notifyAlterTable(diff.leftValue(), diff.rightValue())); - viewsDiff.entriesDiffering().values().forEach(diff -> notifyAlterView(diff.leftValue(), diff.rightValue())); - udfsDiff.entriesDiffering().values().forEach(diff -> notifyAlterFunction(diff.rightValue())); - udasDiff.entriesDiffering().values().forEach(diff -> notifyAlterAggregate(diff.rightValue())); + if (!delta.before.params.equals(delta.after.params)) + notifyAlterKeyspace(delta.before, delta.after); + delta.types.altered.forEach(diff -> notifyAlterType(diff.before, diff.after)); + delta.tables.altered.forEach(diff -> notifyAlterTable(diff.before, diff.after)); + delta.views.altered.forEach(diff -> notifyAlterView(diff.before, diff.after)); + delta.udfs.altered.forEach(diff -> notifyAlterFunction(diff.before, diff.after)); + delta.udas.altered.forEach(diff -> notifyAlterAggregate(diff.before, diff.after)); } private void createKeyspace(KeyspaceMetadata keyspace) @@ -708,7 +720,7 @@ public final class Schema private void dropView(ViewMetadata metadata) { - Keyspace.open(metadata.keyspace).viewManager.stopBuild(metadata.name); + Keyspace.open(metadata.keyspace()).viewManager.stopBuild(metadata.name()); dropTable(metadata.metadata); } @@ -732,7 +744,7 @@ public final class Schema private void createView(ViewMetadata view) { - Keyspace.open(view.keyspace).initCf(metadataRefs.get(view.metadata.id), true); + Keyspace.open(view.keyspace()).initCf(metadataRefs.get(view.metadata.id), true); } private void alterTable(TableMetadata updated) @@ -742,7 +754,7 @@ public final class Schema private void alterView(ViewMetadata updated) { - Keyspace.open(updated.keyspace).getColumnFamilyStore(updated.name).reload(); + Keyspace.open(updated.keyspace()).getColumnFamilyStore(updated.name()).reload(); } private void notifyCreateKeyspace(KeyspaceMetadata ksm) @@ -757,7 +769,7 @@ public final class Schema private void notifyCreateView(ViewMetadata view) { - changeListeners.forEach(l -> l.onCreateView(view.keyspace, view.name)); + changeListeners.forEach(l -> l.onCreateView(view.keyspace(), view.name())); } private void notifyCreateType(UserType ut) @@ -775,36 +787,36 @@ public final class Schema changeListeners.forEach(l -> l.onCreateAggregate(udf.name().keyspace, udf.name().name, udf.argTypes())); } - private void notifyAlterKeyspace(KeyspaceMetadata ksm) + private void notifyAlterKeyspace(KeyspaceMetadata before, KeyspaceMetadata after) { - changeListeners.forEach(l -> l.onAlterKeyspace(ksm.name)); + changeListeners.forEach(l -> l.onAlterKeyspace(after.name)); } - private void notifyAlterTable(TableMetadata current, TableMetadata updated) + private void notifyAlterTable(TableMetadata before, TableMetadata after) { - boolean changeAffectedPreparedStatements = current.changeAffectsPreparedStatements(updated); - changeListeners.forEach(l -> l.onAlterTable(updated.keyspace, updated.name, changeAffectedPreparedStatements)); + boolean changeAffectedPreparedStatements = before.changeAffectsPreparedStatements(after); + changeListeners.forEach(l -> l.onAlterTable(after.keyspace, after.name, changeAffectedPreparedStatements)); } - private void notifyAlterView(ViewMetadata current, ViewMetadata updated) + private void notifyAlterView(ViewMetadata before, ViewMetadata after) { - boolean changeAffectedPreparedStatements = current.metadata.changeAffectsPreparedStatements(updated.metadata); - changeListeners.forEach(l ->l.onAlterView(updated.keyspace, updated.name, changeAffectedPreparedStatements)); + boolean changeAffectedPreparedStatements = before.metadata.changeAffectsPreparedStatements(after.metadata); + changeListeners.forEach(l ->l.onAlterView(after.keyspace(), after.name(), changeAffectedPreparedStatements)); } - private void notifyAlterType(UserType ut) + private void notifyAlterType(UserType before, UserType after) { - changeListeners.forEach(l -> l.onAlterType(ut.keyspace, ut.getNameAsString())); + changeListeners.forEach(l -> l.onAlterType(after.keyspace, after.getNameAsString())); } - private void notifyAlterFunction(UDFunction udf) + private void notifyAlterFunction(UDFunction before, UDFunction after) { - changeListeners.forEach(l -> l.onAlterFunction(udf.name().keyspace, udf.name().name, udf.argTypes())); + changeListeners.forEach(l -> l.onAlterFunction(after.name().keyspace, after.name().name, after.argTypes())); } - private void notifyAlterAggregate(UDAggregate udf) + private void notifyAlterAggregate(UDAggregate before, UDAggregate after) { - changeListeners.forEach(l -> l.onAlterAggregate(udf.name().keyspace, udf.name().name, udf.argTypes())); + changeListeners.forEach(l -> l.onAlterAggregate(after.name().keyspace, after.name().name, after.argTypes())); } private void notifyDropKeyspace(KeyspaceMetadata ksm) @@ -819,7 +831,7 @@ public final class Schema private void notifyDropView(ViewMetadata view) { - changeListeners.forEach(l -> l.onDropView(view.keyspace, view.name)); + changeListeners.forEach(l -> l.onDropView(view.keyspace(), view.name())); } private void notifyDropType(UserType ut)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/SchemaKeyspace.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java index 4945fc2..553ccdc 100644 --- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java @@ -30,20 +30,20 @@ import com.google.common.hash.Hasher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.antlr.runtime.RecognitionException; import org.apache.cassandra.config.*; -import org.apache.cassandra.cql3.statements.CreateTableStatement; -import org.apache.cassandra.schema.ColumnMetadata.ClusteringOrder; import org.apache.cassandra.cql3.*; import org.apache.cassandra.cql3.functions.*; -import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.cql3.statements.schema.CreateTableStatement; import org.apache.cassandra.db.*; +import org.apache.cassandra.db.filter.ColumnFilter; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.db.rows.*; -import org.apache.cassandra.db.filter.ColumnFilter; -import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.service.reads.SpeculativeRetryPolicy; +import org.apache.cassandra.schema.ColumnMetadata.ClusteringOrder; +import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; @@ -53,6 +53,7 @@ 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.cql3.QueryProcessor.executeInternal; import static org.apache.cassandra.cql3.QueryProcessor.executeOnceInternal; @@ -270,6 +271,44 @@ public final class SchemaKeyspace return KeyspaceMetadata.create(SchemaConstants.SCHEMA_KEYSPACE_NAME, KeyspaceParams.local(), org.apache.cassandra.schema.Tables.of(ALL_TABLE_METADATA)); } + static Collection<Mutation> convertSchemaDiffToMutations(KeyspacesDiff diff, long timestamp) + { + Map<String, Mutation> mutations = new HashMap<>(); + + diff.created.forEach(k -> mutations.put(k.name, makeCreateKeyspaceMutation(k, timestamp).build())); + diff.dropped.forEach(k -> mutations.put(k.name, makeDropKeyspaceMutation(k, timestamp).build())); + diff.altered.forEach(kd -> + { + KeyspaceMetadata ks = kd.after; + + Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(ks.name, ks.params, timestamp); + + kd.types.created.forEach(t -> addTypeToSchemaMutation(t, builder)); + kd.types.dropped.forEach(t -> addDropTypeToSchemaMutation(t, builder)); + kd.types.altered(Difference.SHALLOW).forEach(td -> addTypeToSchemaMutation(td.after, builder)); + + kd.tables.created.forEach(t -> addTableToSchemaMutation(t, true, builder)); + kd.tables.dropped.forEach(t -> addDropTableToSchemaMutation(t, builder)); + kd.tables.altered(Difference.SHALLOW).forEach(td -> addAlterTableToSchemaMutation(td.before, td.after, builder)); + + kd.views.created.forEach(v -> addViewToSchemaMutation(v, true, builder)); + kd.views.dropped.forEach(v -> addDropViewToSchemaMutation(v, builder)); + kd.views.altered(Difference.SHALLOW).forEach(vd -> addAlterViewToSchemaMutation(vd.before, vd.after, builder)); + + kd.udfs.created.forEach(f -> addFunctionToSchemaMutation((UDFunction) f, builder)); + kd.udfs.dropped.forEach(f -> addDropFunctionToSchemaMutation((UDFunction) f, builder)); + kd.udfs.altered(Difference.SHALLOW).forEach(fd -> addFunctionToSchemaMutation(fd.after, builder)); + + kd.udas.created.forEach(a -> addAggregateToSchemaMutation((UDAggregate) a, builder)); + kd.udas.dropped.forEach(a -> addDropAggregateToSchemaMutation((UDAggregate) a, builder)); + kd.udas.altered(Difference.SHALLOW).forEach(ad -> addAggregateToSchemaMutation(ad.after, builder)); + + mutations.put(ks.name, builder.build()); + }); + + return mutations.values(); + } + /** * Add entries to system_schema.* for the hardcoded system keyspaces */ @@ -298,7 +337,7 @@ public final class SchemaKeyspace ALL.reverse().forEach(table -> getSchemaCFS(table).truncateBlocking()); } - static void flush() + private static void flush() { if (!DatabaseDescriptor.isUnsafeSystem()) ALL.forEach(table -> FBUtilities.waitOnFuture(getSchemaCFS(table).forceFlush())); @@ -463,15 +502,7 @@ public final class SchemaKeyspace return builder; } - static Mutation.SimpleBuilder makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp) - { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - addTypeToSchemaMutation(type, builder); - return builder; - } - - static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder mutation) + private static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder mutation) { mutation.update(Types) .row(type.getNameAsString()) @@ -479,12 +510,9 @@ public final class SchemaKeyspace .add("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList())); } - static Mutation.SimpleBuilder dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp) + private static void addDropTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder builder) { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); builder.update(Types).row(type.name).delete(); - return builder; } static Mutation.SimpleBuilder makeCreateTableMutation(KeyspaceMetadata keyspace, TableMetadata table, long timestamp) @@ -495,7 +523,7 @@ public final class SchemaKeyspace return builder; } - static void addTableToSchemaMutation(TableMetadata table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder) + private static void addTableToSchemaMutation(TableMetadata table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder) { Row.SimpleBuilder rowBuilder = builder.update(Tables) .row(table.name) @@ -544,13 +572,8 @@ public final class SchemaKeyspace builder.add("cdc", params.cdc); } - static Mutation.SimpleBuilder makeUpdateTableMutation(KeyspaceMetadata keyspace, - TableMetadata oldTable, - TableMetadata newTable, - long timestamp) + private static void addAlterTableToSchemaMutation(TableMetadata oldTable, TableMetadata newTable, Mutation.SimpleBuilder builder) { - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - addTableToSchemaMutation(newTable, false, builder); MapDifference<ByteBuffer, ColumnMetadata> columnDiff = Maps.difference(oldTable.columns, newTable.columns); @@ -602,7 +625,15 @@ public final class SchemaKeyspace // updated indexes need to be updated for (MapDifference.ValueDifference<IndexMetadata> diff : indexesDiff.entriesDiffering().values()) addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), builder); + } + static Mutation.SimpleBuilder makeUpdateTableMutation(KeyspaceMetadata keyspace, + TableMetadata oldTable, + TableMetadata newTable, + long timestamp) + { + Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); + addAlterTableToSchemaMutation(oldTable, newTable, builder); return builder; } @@ -632,7 +663,12 @@ public final class SchemaKeyspace { // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); + addDropTableToSchemaMutation(table, builder); + return builder; + } + private static void addDropTableToSchemaMutation(TableMetadata table, Mutation.SimpleBuilder builder) + { builder.update(Tables).row(table.name).delete(); for (ColumnMetadata column : table.columns()) @@ -646,8 +682,6 @@ public final class SchemaKeyspace for (IndexMetadata index : table.indexes) dropIndexFromSchemaMutation(table, index, builder); - - return builder; } private static void addColumnToSchemaMutation(TableMetadata table, ColumnMetadata column, Mutation.SimpleBuilder builder) @@ -676,7 +710,7 @@ public final class SchemaKeyspace builder.update(DroppedColumns) .row(table.name, column.column.name.toString()) .add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime))) - .add("type", expandUserTypes(column.column.type).asCQL3Type().toString()) + .add("type", column.column.type.asCQL3Type().toString()) .add("kind", column.column.kind.toString().toLowerCase()); } @@ -697,23 +731,15 @@ public final class SchemaKeyspace builder.update(Triggers).row(table.name, trigger.name).delete(); } - static Mutation.SimpleBuilder makeCreateViewMutation(KeyspaceMetadata keyspace, ViewMetadata view, long timestamp) - { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - addViewToSchemaMutation(view, true, builder); - return builder; - } - private static void addViewToSchemaMutation(ViewMetadata view, boolean includeColumns, Mutation.SimpleBuilder builder) { TableMetadata table = view.metadata; Row.SimpleBuilder rowBuilder = builder.update(Views) - .row(view.name) + .row(view.name()) .add("include_all_columns", view.includeAllColumns) .add("base_table_id", view.baseTableId.asUUID()) .add("base_table_name", view.baseTableName) - .add("where_clause", view.whereClause) + .add("where_clause", view.whereClause.toString()) .add("id", table.id.asUUID()); addTableParamsToRowBuilder(table.params, rowBuilder); @@ -728,57 +754,32 @@ public final class SchemaKeyspace } } - static Mutation.SimpleBuilder makeDropViewMutation(KeyspaceMetadata keyspace, ViewMetadata view, long timestamp) + private static void addDropViewToSchemaMutation(ViewMetadata view, Mutation.SimpleBuilder builder) { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - - builder.update(Views).row(view.name).delete(); + builder.update(Views).row(view.name()).delete(); TableMetadata table = view.metadata; for (ColumnMetadata column : table.columns()) dropColumnFromSchemaMutation(table, column, builder); - - for (IndexMetadata index : table.indexes) - dropIndexFromSchemaMutation(table, index, builder); - - return builder; } - public static Mutation.SimpleBuilder makeUpdateViewMutation(Mutation.SimpleBuilder builder, - ViewMetadata oldView, - ViewMetadata newView) + private static void addAlterViewToSchemaMutation(ViewMetadata before, ViewMetadata after, Mutation.SimpleBuilder builder) { - addViewToSchemaMutation(newView, false, builder); + addViewToSchemaMutation(after, false, builder); - MapDifference<ByteBuffer, ColumnMetadata> columnDiff = Maps.difference(oldView.metadata.columns, - newView.metadata.columns); + MapDifference<ByteBuffer, ColumnMetadata> columnDiff = Maps.difference(before.metadata.columns, after.metadata.columns); // columns that are no longer needed for (ColumnMetadata column : columnDiff.entriesOnlyOnLeft().values()) - dropColumnFromSchemaMutation(oldView.metadata, column, builder); + dropColumnFromSchemaMutation(before.metadata, column, builder); // newly added columns for (ColumnMetadata column : columnDiff.entriesOnlyOnRight().values()) - addColumnToSchemaMutation(newView.metadata, column, builder); + addColumnToSchemaMutation(after.metadata, column, builder); // old columns with updated attributes for (ByteBuffer name : columnDiff.entriesDiffering().keySet()) - addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumn(name), builder); - - // dropped columns - MapDifference<ByteBuffer, DroppedColumn> droppedColumnDiff = - Maps.difference(oldView.metadata.droppedColumns, oldView.metadata.droppedColumns); - - // newly dropped columns - for (DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values()) - addDroppedColumnToSchemaMutation(oldView.metadata, column, builder); - - // columns added then dropped again - for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet()) - addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.droppedColumns.get(name), builder); - - return builder; + addColumnToSchemaMutation(after.metadata, after.metadata.getColumn(name), builder); } private static void addIndexToSchemaMutation(TableMetadata table, IndexMetadata index, Mutation.SimpleBuilder builder) @@ -801,15 +802,7 @@ public final class SchemaKeyspace addIndexToSchemaMutation(table, index, builder); } - static Mutation.SimpleBuilder makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp) - { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - addFunctionToSchemaMutation(function, builder); - return builder; - } - - static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder) + private static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder) { builder.update(Functions) .row(function.name().name, function.argumentsList()) @@ -832,23 +825,12 @@ public final class SchemaKeyspace } } - static Mutation.SimpleBuilder makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp) + private static void addDropFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder) { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); builder.update(Functions).row(function.name().name, function.argumentsList()).delete(); - return builder; } - static Mutation.SimpleBuilder makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp) - { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); - addAggregateToSchemaMutation(aggregate, builder); - return builder; - } - - static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder) + private static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder) { builder.update(Aggregates) .row(aggregate.name().name, aggregate.argumentsList()) @@ -862,12 +844,9 @@ public final class SchemaKeyspace : null); } - static Mutation.SimpleBuilder makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp) + private static void addDropAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder) { - // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). - Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); builder.update(Aggregates).row(aggregate.name().name, aggregate.argumentsList()).delete(); - return builder; } /* @@ -1017,7 +996,7 @@ public final class SchemaKeyspace String query = format("SELECT * FROM %s.%s WHERE keyspace_name = ? AND table_name = ?", SchemaConstants.SCHEMA_KEYSPACE_NAME, COLUMNS); UntypedResultSet columnRows = query(query, keyspace, table); if (columnRows.isEmpty()) - throw new MissingColumns("Columns not found in schema table for " + keyspace + "." + table); + throw new MissingColumns("Columns not found in schema table for " + keyspace + '.' + table); List<ColumnMetadata> columns = new ArrayList<>(); columnRows.forEach(row -> columns.add(createColumnFromRow(row, types))); @@ -1120,7 +1099,7 @@ public final class SchemaKeyspace Views.Builder views = org.apache.cassandra.schema.Views.builder(); for (UntypedResultSet.Row row : query(query, keyspaceName)) - views.add(fetchView(keyspaceName, row.getString("view_name"), types)); + views.put(fetchView(keyspaceName, row.getString("view_name"), types)); return views.build(); } @@ -1135,7 +1114,7 @@ public final class SchemaKeyspace TableId baseTableId = TableId.fromUUID(row.getUUID("base_table_id")); String baseTableName = row.getString("base_table_name"); boolean includeAll = row.getBoolean("include_all_columns"); - String whereClause = row.getString("where_clause"); + String whereClauseString = row.getString("where_clause"); List<ColumnMetadata> columns = fetchColumns(keyspaceName, viewName, types); @@ -1147,31 +1126,36 @@ public final class SchemaKeyspace .params(createTableParamsFromRow(row)) .build(); - String rawSelect = View.buildSelectStatement(baseTableName, columns, whereClause); - SelectStatement.RawStatement rawStatement = (SelectStatement.RawStatement) QueryProcessor.parseStatement(rawSelect); + WhereClause whereClause; - return new ViewMetadata(keyspaceName, viewName, baseTableId, baseTableName, includeAll, rawStatement, whereClause, metadata); + try + { + whereClause = WhereClause.parse(whereClauseString); + } + catch (RecognitionException e) + { + throw new RuntimeException(format("Unexpected error while parsing materialized view's where clause for '%s' (got %s)", viewName, whereClauseString)); + } + + return new ViewMetadata(baseTableId, baseTableName, includeAll, whereClause, metadata); } private static Functions fetchFunctions(String keyspaceName, Types types) { - Functions udfs = fetchUDFs(keyspaceName, types); - Functions udas = fetchUDAs(keyspaceName, udfs, types); + Collection<UDFunction> udfs = fetchUDFs(keyspaceName, types); + Collection<UDAggregate> udas = fetchUDAs(keyspaceName, udfs, types); - return org.apache.cassandra.schema.Functions.builder() - .add(udfs) - .add(udas) - .build(); + return org.apache.cassandra.schema.Functions.builder().add(udfs).add(udas).build(); } - private static Functions fetchUDFs(String keyspaceName, Types types) + private static Collection<UDFunction> fetchUDFs(String keyspaceName, Types types) { String query = format("SELECT * FROM %s.%s WHERE keyspace_name = ?", SchemaConstants.SCHEMA_KEYSPACE_NAME, FUNCTIONS); - Functions.Builder functions = org.apache.cassandra.schema.Functions.builder(); + Collection<UDFunction> functions = new ArrayList<>(); for (UntypedResultSet.Row row : query(query, keyspaceName)) functions.add(createUDFFromRow(row, types)); - return functions.build(); + return functions; } private static UDFunction createUDFFromRow(UntypedResultSet.Row row, Types types) @@ -1230,17 +1214,16 @@ public final class SchemaKeyspace } } - private static Functions fetchUDAs(String keyspaceName, Functions udfs, Types types) + private static Collection<UDAggregate> fetchUDAs(String keyspaceName, Collection<UDFunction> udfs, Types types) { String query = format("SELECT * FROM %s.%s WHERE keyspace_name = ?", SchemaConstants.SCHEMA_KEYSPACE_NAME, AGGREGATES); - Functions.Builder aggregates = org.apache.cassandra.schema.Functions.builder(); - for (UntypedResultSet.Row row : query(query, keyspaceName)) - aggregates.add(createUDAFromRow(row, udfs, types)); - return aggregates.build(); + Collection<UDAggregate> aggregates = new ArrayList<>(); + query(query, keyspaceName).forEach(row -> aggregates.add(createUDAFromRow(row, udfs, types))); + return aggregates; } - private static UDAggregate createUDAFromRow(UntypedResultSet.Row row, Functions functions, Types types) + private static UDAggregate createUDAFromRow(UntypedResultSet.Row row, Collection<UDFunction> functions, Types types) { String ksName = row.getString("keyspace_name"); String functionName = row.getString("aggregate_name"); @@ -1255,18 +1238,12 @@ public final class SchemaKeyspace AbstractType<?> returnType = CQLTypeParser.parse(ksName, row.getString("return_type"), types); FunctionName stateFunc = new FunctionName(ksName, (row.getString("state_func"))); + FunctionName finalFunc = row.has("final_func") ? new FunctionName(ksName, row.getString("final_func")) : null; AbstractType<?> stateType = row.has("state_type") ? CQLTypeParser.parse(ksName, row.getString("state_type"), types) : null; ByteBuffer initcond = row.has("initcond") ? Terms.asBytes(ksName, row.getString("initcond"), stateType) : null; - try - { - return UDAggregate.create(functions, name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); - } - catch (InvalidRequestException reason) - { - return UDAggregate.createBroken(name, argTypes, returnType, initcond, reason); - } + return UDAggregate.create(functions, name, argTypes, returnType, stateFunc, finalFunc, stateType, initcond); } private static UntypedResultSet query(String query, Object... variables) @@ -1310,52 +1287,6 @@ public final class SchemaKeyspace return keyspaces.build(); } - /* - * Type parsing and transformation - */ - - /* - * Recursively replaces any instances of UserType with an equivalent TupleType. - * We do it for dropped_columns, to allow safely dropping unused user types without retaining any references - * in dropped_columns. - */ - private static AbstractType<?> expandUserTypes(AbstractType<?> original) - { - if (original instanceof UserType) - return new TupleType(expandUserTypes(((UserType) original).fieldTypes())); - - if (original instanceof TupleType) - return new TupleType(expandUserTypes(((TupleType) original).allTypes())); - - if (original instanceof ListType<?>) - return ListType.getInstance(expandUserTypes(((ListType<?>) original).getElementsType()), original.isMultiCell()); - - if (original instanceof MapType<?,?>) - { - MapType<?, ?> mt = (MapType<?, ?>) original; - return MapType.getInstance(expandUserTypes(mt.getKeysType()), expandUserTypes(mt.getValuesType()), mt.isMultiCell()); - } - - if (original instanceof SetType<?>) - return SetType.getInstance(expandUserTypes(((SetType<?>) original).getElementsType()), original.isMultiCell()); - - // this is very unlikely to ever happen, but it's better to be safe than sorry - if (original instanceof ReversedType<?>) - return ReversedType.getInstance(expandUserTypes(((ReversedType) original).baseType)); - - if (original instanceof CompositeType) - return CompositeType.getInstance(expandUserTypes(original.getComponents())); - - return original; - } - - private static List<AbstractType<?>> expandUserTypes(List<AbstractType<?>> types) - { - return types.stream() - .map(SchemaKeyspace::expandUserTypes) - .collect(toList()); - } - @VisibleForTesting static class MissingColumns extends RuntimeException { http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/SchemaTransformation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/SchemaTransformation.java b/src/java/org/apache/cassandra/schema/SchemaTransformation.java new file mode 100644 index 0000000..c19ac7c --- /dev/null +++ b/src/java/org/apache/cassandra/schema/SchemaTransformation.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.schema; + +public interface SchemaTransformation +{ + /** + * Apply a statement transformation to a schema snapshot. + * + * Implementing methods should be side-effect free. + * + * @param schema Keyspaces to base the transformation on + * @return Keyspaces transformed by the statement + */ + Keyspaces apply(Keyspaces schema); +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/TableId.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/TableId.java b/src/java/org/apache/cassandra/schema/TableId.java index 95256fe..695147f 100644 --- a/src/java/org/apache/cassandra/schema/TableId.java +++ b/src/java/org/apache/cassandra/schema/TableId.java @@ -30,7 +30,7 @@ import org.apache.cassandra.utils.UUIDGen; /** * The unique identifier of a table. * <p> - * This is essentially a UUID, but we wrap it as it's used quite a bit in the code and having a nicely name class make + * This is essentially a UUID, but we wrap it as it's used quite a bit in the code and having a nicely named class make * the code more readable. */ public class TableId http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/TableMetadata.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/TableMetadata.java b/src/java/org/apache/cassandra/schema/TableMetadata.java index 47e5b47..6466e2e 100644 --- a/src/java/org/apache/cassandra/schema/TableMetadata.java +++ b/src/java/org/apache/cassandra/schema/TableMetadata.java @@ -44,6 +44,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; +import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.transform; import static org.apache.cassandra.schema.IndexMetadata.isNameValid; @@ -204,6 +205,21 @@ public final class TableMetadata return kind == Kind.INDEX; } + public TableMetadata withSwapped(TableParams params) + { + return unbuild().params(params).build(); + } + + public TableMetadata withSwapped(Triggers triggers) + { + return unbuild().triggers(triggers).build(); + } + + public TableMetadata withSwapped(Indexes indexes) + { + return unbuild().indexes(indexes).build(); + } + public boolean isView() { return kind == Kind.VIEW; @@ -418,42 +434,62 @@ public final class TableMetadata indexes.validate(this); } - void validateCompatibility(TableMetadata other) + void validateCompatibility(TableMetadata previous) { if (isIndex()) return; - if (!other.keyspace.equals(keyspace)) - except("Keyspace mismatch (found %s; expected %s)", other.keyspace, keyspace); + if (!previous.keyspace.equals(keyspace)) + except("Keyspace mismatch (found %s; expected %s)", keyspace, previous.keyspace); - if (!other.name.equals(name)) - except("Table mismatch (found %s; expected %s)", other.name, name); + if (!previous.name.equals(name)) + except("Table mismatch (found %s; expected %s)", name, previous.name); - if (!other.id.equals(id)) - except("Table ID mismatch (found %s; expected %s)", other.id, id); + if (!previous.id.equals(id)) + except("Table ID mismatch (found %s; expected %s)", id, previous.id); - if (!other.flags.equals(flags)) - except("Table type mismatch (found %s; expected %s)", other.flags, flags); + if (!previous.flags.equals(flags)) + except("Table type mismatch (found %s; expected %s)", flags, previous.flags); - if (other.partitionKeyColumns.size() != partitionKeyColumns.size()) - except("Partition keys of different length (found %s; expected %s)", other.partitionKeyColumns.size(), partitionKeyColumns.size()); + if (previous.partitionKeyColumns.size() != partitionKeyColumns.size()) + { + except("Partition keys of different length (found %s; expected %s)", + partitionKeyColumns.size(), + previous.partitionKeyColumns.size()); + } for (int i = 0; i < partitionKeyColumns.size(); i++) - if (!other.partitionKeyColumns.get(i).type.isCompatibleWith(partitionKeyColumns.get(i).type)) - except("Partition key column mismatch (found %s; expected %s)", other.partitionKeyColumns.get(i).type, partitionKeyColumns.get(i).type); + { + if (!partitionKeyColumns.get(i).type.isCompatibleWith(previous.partitionKeyColumns.get(i).type)) + { + except("Partition key column mismatch (found %s; expected %s)", + partitionKeyColumns.get(i).type, + previous.partitionKeyColumns.get(i).type); + } + } - if (other.clusteringColumns.size() != clusteringColumns.size()) - except("Clustering columns of different length (found %s; expected %s)", other.clusteringColumns.size(), clusteringColumns.size()); + if (previous.clusteringColumns.size() != clusteringColumns.size()) + { + except("Clustering columns of different length (found %s; expected %s)", + clusteringColumns.size(), + previous.clusteringColumns.size()); + } for (int i = 0; i < clusteringColumns.size(); i++) - if (!other.clusteringColumns.get(i).type.isCompatibleWith(clusteringColumns.get(i).type)) - except("Clustering column mismatch (found %s; expected %s)", other.clusteringColumns.get(i).type, clusteringColumns.get(i).type); + { + if (!clusteringColumns.get(i).type.isCompatibleWith(previous.clusteringColumns.get(i).type)) + { + except("Clustering column mismatch (found %s; expected %s)", + clusteringColumns.get(i).type, + previous.clusteringColumns.get(i).type); + } + } - for (ColumnMetadata otherColumn : other.regularAndStaticColumns) + for (ColumnMetadata previousColumn : previous.regularAndStaticColumns) { - ColumnMetadata column = getColumn(otherColumn.name); - if (column != null && !otherColumn.type.isCompatibleWith(column.type)) - except("Column mismatch (found %s; expected %s", otherColumn, column); + ColumnMetadata column = getColumn(previousColumn.name); + if (column != null && !column.type.isCompatibleWith(previousColumn.type)) + except("Column mismatch (found %s; expected %s)", column, previousColumn); } } @@ -472,7 +508,7 @@ public final class TableMetadata * This method should only be called for superColumn tables and "static * compact" ones. For any other table, all column names are UTF8. */ - public AbstractType<?> staticCompactOrSuperTableColumnNameType() + AbstractType<?> staticCompactOrSuperTableColumnNameType() { if (isSuper()) { @@ -543,6 +579,22 @@ public final class TableMetadata return unbuild().params(builder.build()).build(); } + boolean referencesUserType(ByteBuffer name) + { + return any(columns(), c -> c.type.referencesUserType(name)); + } + + public TableMetadata withUpdatedUserType(UserType udt) + { + if (!referencesUserType(udt.name)) + return this; + + Builder builder = unbuild(); + columns().forEach(c -> builder.alterColumnType(c.name, c.type.withUpdatedUserType(udt))); + + return builder.build(); + } + private void except(String format, Object... args) { throw new ConfigurationException(keyspace + "." + name + ": " + format(format, args)); @@ -559,6 +611,11 @@ public final class TableMetadata TableMetadata tm = (TableMetadata) o; + return equalsWithoutColumns(tm) && columns.equals(tm.columns); + } + + private boolean equalsWithoutColumns(TableMetadata tm) + { return keyspace.equals(tm.keyspace) && name.equals(tm.name) && id.equals(tm.id) @@ -566,12 +623,46 @@ public final class TableMetadata && kind == tm.kind && params.equals(tm.params) && flags.equals(tm.flags) - && columns.equals(tm.columns) && droppedColumns.equals(tm.droppedColumns) && indexes.equals(tm.indexes) && triggers.equals(tm.triggers); } + Optional<Difference> compare(TableMetadata other) + { + return equalsWithoutColumns(other) + ? compareColumns(other.columns) + : Optional.of(Difference.SHALLOW); + } + + private Optional<Difference> compareColumns(Map<ByteBuffer, ColumnMetadata> other) + { + if (!columns.keySet().equals(other.keySet())) + return Optional.of(Difference.SHALLOW); + + boolean differsDeeply = false; + + for (Map.Entry<ByteBuffer, ColumnMetadata> entry : columns.entrySet()) + { + ColumnMetadata thisColumn = entry.getValue(); + ColumnMetadata thatColumn = other.get(entry.getKey()); + + Optional<Difference> difference = thisColumn.compare(thatColumn); + if (difference.isPresent()) + { + switch (difference.get()) + { + case SHALLOW: + return difference; + case DEEP: + differsDeeply = true; + } + } + } + + return differsDeeply ? Optional.of(Difference.DEEP) : Optional.empty(); + } + @Override public int hashCode() { @@ -858,7 +949,7 @@ public final class TableMetadata return this; } - public Builder addColumns(Iterable<ColumnMetadata> columns) + Builder addColumns(Iterable<ColumnMetadata> columns) { columns.forEach(this::addColumn); return this; @@ -884,7 +975,7 @@ public final class TableMetadata public Builder recordColumnDrop(ColumnMetadata column, long timeMicros) { - droppedColumns.put(column.name.bytes, new DroppedColumn(column, timeMicros)); + droppedColumns.put(column.name.bytes, new DroppedColumn(column.withNewType(column.type.expandUserTypes()), timeMicros)); return this; } @@ -950,7 +1041,7 @@ public final class TableMetadata return this; } - public Builder alterColumnType(ColumnIdentifier name, AbstractType<?> type) + Builder alterColumnType(ColumnIdentifier name, AbstractType<?> type) { ColumnMetadata column = columns.get(name.bytes); if (column == null) @@ -986,6 +1077,8 @@ public final class TableMetadata * Currently this is only used by views with normal base column as PK column * so updates to other columns do not make the row live when the base column * is not live. See CASSANDRA-11500. + * + * TODO: does not belong here, should be gone */ public boolean enforceStrictLiveness() { http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/TableMetadataRef.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/TableMetadataRef.java b/src/java/org/apache/cassandra/schema/TableMetadataRef.java index 5ff9d5b..3c45594 100644 --- a/src/java/org/apache/cassandra/schema/TableMetadataRef.java +++ b/src/java/org/apache/cassandra/schema/TableMetadataRef.java @@ -66,7 +66,7 @@ public final class TableMetadataRef */ void set(TableMetadata metadata) { - get().validateCompatibility(metadata); + metadata.validateCompatibility(get()); this.metadata = metadata; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/TableParams.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/TableParams.java b/src/java/org/apache/cassandra/schema/TableParams.java index 78dc894..f5b3c89 100644 --- a/src/java/org/apache/cassandra/schema/TableParams.java +++ b/src/java/org/apache/cassandra/schema/TableParams.java @@ -245,7 +245,7 @@ public final class TableParams { private String comment = ""; private Double bloomFilterFpChance; - public Double crcCheckChance = 1.0; + private double crcCheckChance = 1.0; private int gcGraceSeconds = 864000; // 10 days private int defaultTimeToLive = 0; private int memtableFlushPeriodInMs = 0; http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/Tables.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/Tables.java b/src/java/org/apache/cassandra/schema/Tables.java index a83c061..0320440 100644 --- a/src/java/org/apache/cassandra/schema/Tables.java +++ b/src/java/org/apache/cassandra/schema/Tables.java @@ -17,32 +17,38 @@ */ package org.apache.cassandra.schema; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; import javax.annotation.Nullable; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.MapDifference; -import com.google.common.collect.Maps; +import com.google.common.collect.*; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.index.internal.CassandraIndex; -import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.transform; /** * An immutable container for a keyspace's Tables. */ public final class Tables implements Iterable<TableMetadata> { + private static final Tables NONE = builder().build(); + private final ImmutableMap<String, TableMetadata> tables; + private final ImmutableMap<TableId, TableMetadata> tablesById; private final ImmutableMap<String, TableMetadata> indexTables; private Tables(Builder builder) { tables = builder.tables.build(); + tablesById = builder.tablesById.build(); indexTables = builder.indexTables.build(); } @@ -53,7 +59,7 @@ public final class Tables implements Iterable<TableMetadata> public static Tables none() { - return builder().build(); + return NONE; } public static Tables of(TableMetadata... tables) @@ -71,6 +77,11 @@ public final class Tables implements Iterable<TableMetadata> return tables.values().iterator(); } + public Iterable<TableMetadata> referencingUserType(ByteBuffer name) + { + return Iterables.filter(tables.values(), t -> t.referencesUserType(name)); + } + ImmutableMap<String, TableMetadata> indexTables() { return indexTables; @@ -105,9 +116,21 @@ public final class Tables implements Iterable<TableMetadata> } @Nullable - public TableMetadata getIndexTableNullable(String name) + TableMetadata getNullable(TableId id) { - return indexTables.get(name); + return tablesById.get(id); + } + + boolean containsTable(TableId id) + { + return tablesById.containsKey(id); + } + + public Tables filter(Predicate<TableMetadata> predicate) + { + Builder builder = builder(); + tables.values().stream().filter(predicate).forEach(builder::add); + return builder.build(); } /** @@ -134,18 +157,19 @@ public final class Tables implements Iterable<TableMetadata> TableMetadata table = get(name).orElseThrow(() -> new IllegalStateException(String.format("Table %s doesn't exists", name))); - return builder().add(filter(this, t -> t != table)).build(); + return without(table); } - MapDifference<TableId, TableMetadata> diff(Tables other) + public Tables without(TableMetadata table) { - Map<TableId, TableMetadata> thisTables = new HashMap<>(); - this.forEach(t -> thisTables.put(t.id, t)); - - Map<TableId, TableMetadata> otherTables = new HashMap<>(); - other.forEach(t -> otherTables.put(t.id, t)); + return filter(t -> t != table); + } - return Maps.difference(thisTables, otherTables); + public Tables withUpdatedUserType(UserType udt) + { + return any(this, t -> t.referencesUserType(udt.name)) + ? builder().add(transform(this, t -> t.withUpdatedUserType(udt))).build() + : this; } MapDifference<String, TableMetadata> indexesDiff(Tables other) @@ -180,6 +204,7 @@ public final class Tables implements Iterable<TableMetadata> public static final class Builder { final ImmutableMap.Builder<String, TableMetadata> tables = new ImmutableMap.Builder<>(); + final ImmutableMap.Builder<TableId, TableMetadata> tablesById = new ImmutableMap.Builder<>(); final ImmutableMap.Builder<String, TableMetadata> indexTables = new ImmutableMap.Builder<>(); private Builder() @@ -195,6 +220,8 @@ public final class Tables implements Iterable<TableMetadata> { tables.put(table.name, table); + tablesById.put(table.id, table); + table.indexes .stream() .filter(i -> !i.isCustom()) @@ -217,4 +244,38 @@ public final class Tables implements Iterable<TableMetadata> return this; } } + + static TablesDiff diff(Tables before, Tables after) + { + return TablesDiff.diff(before, after); + } + + public static final class TablesDiff extends Diff<Tables, TableMetadata> + { + private final static TablesDiff NONE = new TablesDiff(Tables.none(), Tables.none(), ImmutableList.of()); + + private TablesDiff(Tables created, Tables dropped, ImmutableCollection<Altered<TableMetadata>> altered) + { + super(created, dropped, altered); + } + + private static TablesDiff diff(Tables before, Tables after) + { + if (before == after) + return NONE; + + Tables created = after.filter(t -> !before.containsTable(t.id)); + Tables dropped = before.filter(t -> !after.containsTable(t.id)); + + ImmutableList.Builder<Altered<TableMetadata>> altered = ImmutableList.builder(); + before.forEach(tableBefore -> + { + TableMetadata tableAfter = after.getNullable(tableBefore.id); + if (null != tableAfter) + tableBefore.compare(tableAfter).ifPresent(kind -> altered.add(new Altered<>(tableBefore, tableAfter, kind))); + }); + + return new TablesDiff(created, dropped, altered.build()); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/Types.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/Types.java b/src/java/org/apache/cassandra/schema/Types.java index 0bdf7cf..64aeead 100644 --- a/src/java/org/apache/cassandra/schema/Types.java +++ b/src/java/org/apache/cassandra/schema/Types.java @@ -19,6 +19,7 @@ package org.apache.cassandra.schema; import java.nio.ByteBuffer; import java.util.*; +import java.util.function.Predicate; import javax.annotation.Nullable; @@ -31,8 +32,11 @@ import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.exceptions.ConfigurationException; import static java.lang.String.format; -import static com.google.common.collect.Iterables.filter; import static java.util.stream.Collectors.toList; + +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.transform; + import static org.apache.cassandra.utils.ByteBufferUtil.bytes; /** @@ -82,6 +86,11 @@ public final class Types implements Iterable<UserType> return types.values().iterator(); } + public Iterable<UserType> referencingUserType(ByteBuffer name) + { + return Iterables.filter(types.values(), t -> t.referencesUserType(name) && !t.name.equals(name)); + } + /** * Get the type with the specified name * @@ -105,6 +114,18 @@ public final class Types implements Iterable<UserType> return types.get(name); } + boolean containsType(ByteBuffer name) + { + return types.containsKey(name); + } + + Types filter(Predicate<UserType> predicate) + { + Builder builder = builder(); + types.values().stream().filter(predicate).forEach(builder::add); + return builder.build(); + } + /** * Create a Types instance with the provided type added */ @@ -124,12 +145,19 @@ public final class Types implements Iterable<UserType> UserType type = get(name).orElseThrow(() -> new IllegalStateException(format("Type %s doesn't exists", name))); - return builder().add(filter(this, t -> t != type)).build(); + return without(type); } - MapDifference<ByteBuffer, UserType> diff(Types other) + public Types without(UserType type) { - return Maps.difference(types, other.types); + return filter(t -> t != type); + } + + public Types withUpdatedUserType(UserType udt) + { + return any(this, t -> t.referencesUserType(udt.name)) + ? builder().add(transform(this, t -> t.withUpdatedUserType(udt))).build() + : this; } @Override @@ -155,7 +183,7 @@ public final class Types implements Iterable<UserType> if (!thisNext.getKey().equals(otherNext.getKey())) return false; - if (!thisNext.getValue().equals(otherNext.getValue(), true)) // ignore freezing + if (!thisNext.getValue().equals(otherNext.getValue())) return false; } return true; @@ -305,7 +333,7 @@ public final class Types implements Iterable<UserType> { List<FieldIdentifier> preparedFieldNames = fieldNames.stream() - .map(t -> FieldIdentifier.forInternalString(t)) + .map(FieldIdentifier::forInternalString) .collect(toList()); List<AbstractType<?>> preparedFieldTypes = @@ -329,4 +357,38 @@ public final class Types implements Iterable<UserType> } } } + + static TypesDiff diff(Types before, Types after) + { + return TypesDiff.diff(before, after); + } + + static final class TypesDiff extends Diff<Types, UserType> + { + private static final TypesDiff NONE = new TypesDiff(Types.none(), Types.none(), ImmutableList.of()); + + private TypesDiff(Types created, Types dropped, ImmutableCollection<Altered<UserType>> altered) + { + super(created, dropped, altered); + } + + private static TypesDiff diff(Types before, Types after) + { + if (before == after) + return NONE; + + Types created = after.filter(t -> !before.containsType(t.name)); + Types dropped = before.filter(t -> !after.containsType(t.name)); + + ImmutableList.Builder<Altered<UserType>> altered = ImmutableList.builder(); + before.forEach(typeBefore -> + { + UserType typeAfter = after.getNullable(typeBefore.name); + if (null != typeAfter) + typeBefore.compare(typeAfter).ifPresent(kind -> altered.add(new Altered<>(typeBefore, typeAfter, kind))); + }); + + return new TypesDiff(created, dropped, altered.build()); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/ViewMetadata.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/ViewMetadata.java b/src/java/org/apache/cassandra/schema/ViewMetadata.java index 57f4092..66360bf 100644 --- a/src/java/org/apache/cassandra/schema/ViewMetadata.java +++ b/src/java/org/apache/cassandra/schema/ViewMetadata.java @@ -17,56 +17,52 @@ */ package org.apache.cassandra.schema; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.antlr.runtime.*; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.cql3.statements.SelectStatement; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.db.view.View; -import org.apache.cassandra.exceptions.SyntaxException; +import java.nio.ByteBuffer; +import java.util.Optional; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.db.marshal.UserType; + public final class ViewMetadata { - public final String keyspace; - public final String name; public final TableId baseTableId; public final String baseTableName; + public final boolean includeAllColumns; public final TableMetadata metadata; - public final SelectStatement.RawStatement select; - public final String whereClause; + public final WhereClause whereClause; /** - * @param name Name of the view * @param baseTableId Internal ID of the table which this view is based off of * @param includeAllColumns Whether to include all columns or not */ - public ViewMetadata(String keyspace, - String name, - TableId baseTableId, + public ViewMetadata(TableId baseTableId, String baseTableName, boolean includeAllColumns, - SelectStatement.RawStatement select, - String whereClause, + WhereClause whereClause, TableMetadata metadata) { - this.keyspace = keyspace; - this.name = name; this.baseTableId = baseTableId; this.baseTableName = baseTableName; this.includeAllColumns = includeAllColumns; - this.select = select; this.whereClause = whereClause; this.metadata = metadata; } + public String keyspace() + { + return metadata.keyspace; + } + + public String name() + { + return metadata.name; + } + /** * @return true if the view specified by this definition will include the column, false otherwise */ @@ -77,7 +73,7 @@ public final class ViewMetadata public ViewMetadata copy(TableMetadata newMetadata) { - return new ViewMetadata(keyspace, name, baseTableId, baseTableName, includeAllColumns, select, whereClause, newMetadata); + return new ViewMetadata(baseTableId, baseTableName, includeAllColumns, whereClause, newMetadata); } public TableMetadata baseTableMetadata() @@ -95,20 +91,24 @@ public final class ViewMetadata return false; ViewMetadata other = (ViewMetadata) o; - return Objects.equals(keyspace, other.keyspace) - && Objects.equals(name, other.name) - && Objects.equals(baseTableId, other.baseTableId) - && Objects.equals(includeAllColumns, other.includeAllColumns) - && Objects.equals(whereClause, other.whereClause) - && Objects.equals(metadata, other.metadata); + return baseTableId.equals(other.baseTableId) + && includeAllColumns == other.includeAllColumns + && whereClause.equals(other.whereClause) + && metadata.equals(other.metadata); + } + + Optional<Difference> compare(ViewMetadata other) + { + if (!baseTableId.equals(other.baseTableId) || includeAllColumns != other.includeAllColumns || !whereClause.equals(other.whereClause)) + return Optional.of(Difference.SHALLOW); + + return metadata.compare(other.metadata); } @Override public int hashCode() { return new HashCodeBuilder(29, 1597) - .append(keyspace) - .append(name) .append(baseTableId) .append(includeAllColumns) .append(whereClause) @@ -120,8 +120,6 @@ public final class ViewMetadata public String toString() { return new ToStringBuilder(this) - .append("keyspace", keyspace) - .append("name", name) .append("baseTableId", baseTableId) .append("baseTableName", baseTableName) .append("includeAllColumns", includeAllColumns) @@ -130,68 +128,37 @@ public final class ViewMetadata .toString(); } - /** - * Replace the column 'from' with 'to' in this materialized view definition's partition, - * clustering, or included columns. - * @param from the existing column - * @param to the new column - */ - public ViewMetadata renamePrimaryKeyColumn(ColumnIdentifier from, ColumnIdentifier to) + public boolean referencesUserType(ByteBuffer name) + { + return metadata.referencesUserType(name); + } + + public ViewMetadata withUpdatedUserType(UserType udt) + { + return referencesUserType(udt.name) + ? copy(metadata.withUpdatedUserType(udt)) + : this; + } + + public ViewMetadata withRenamedPrimaryKeyColumn(ColumnIdentifier from, ColumnIdentifier to) { // convert whereClause to Relations, rename ids in Relations, then convert back to whereClause - List<Relation> relations = whereClauseToRelations(whereClause); - ColumnMetadata.Raw fromRaw = ColumnMetadata.Raw.forQuoted(from.toString()); - ColumnMetadata.Raw toRaw = ColumnMetadata.Raw.forQuoted(to.toString()); - List<Relation> newRelations = - relations.stream() - .map(r -> r.renameIdentifier(fromRaw, toRaw)) - .collect(Collectors.toList()); - - String rawSelect = View.buildSelectStatement(baseTableName, metadata.columns(), whereClause); - - return new ViewMetadata(keyspace, - name, - baseTableId, + ColumnMetadata.Raw rawFrom = ColumnMetadata.Raw.forQuoted(from.toString()); + ColumnMetadata.Raw rawTo = ColumnMetadata.Raw.forQuoted(to.toString()); + + return new ViewMetadata(baseTableId, baseTableName, includeAllColumns, - (SelectStatement.RawStatement) QueryProcessor.parseStatement(rawSelect), - View.relationsToWhereClause(newRelations), + whereClause.renameIdentifier(rawFrom, rawTo), metadata.unbuild().renamePrimaryKeyColumn(from, to).build()); } public ViewMetadata withAddedRegularColumn(ColumnMetadata column) { - return new ViewMetadata(keyspace, - name, - baseTableId, + return new ViewMetadata(baseTableId, baseTableName, includeAllColumns, - select, whereClause, metadata.unbuild().addColumn(column).build()); } - - public ViewMetadata withAlteredColumnType(ColumnIdentifier name, AbstractType<?> type) - { - return new ViewMetadata(keyspace, - this.name, - baseTableId, - baseTableName, - includeAllColumns, - select, - whereClause, - metadata.unbuild().alterColumnType(name, type).build()); - } - - private static List<Relation> whereClauseToRelations(String whereClause) - { - try - { - return CQLFragmentParser.parseAnyUnhandled(CqlParser::whereClause, whereClause).build().relations; - } - catch (RecognitionException | SyntaxException exc) - { - throw new RuntimeException("Unexpected error parsing materialized view's where clause while handling column rename: ", exc); - } - } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/schema/Views.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/Views.java b/src/java/org/apache/cassandra/schema/Views.java index 5765433..07cd8f2 100644 --- a/src/java/org/apache/cassandra/schema/Views.java +++ b/src/java/org/apache/cassandra/schema/Views.java @@ -21,24 +21,26 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Optional; -import java.util.UUID; +import java.util.function.Predicate; import javax.annotation.Nullable; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.MapDifference; -import com.google.common.collect.Maps; +import com.google.common.collect.*; -import static com.google.common.collect.Iterables.filter; +import org.apache.cassandra.db.marshal.UserType; + +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.transform; public final class Views implements Iterable<ViewMetadata> { + private static final Views NONE = builder().build(); + private final ImmutableMap<String, ViewMetadata> views; private Views(Builder builder) { - views = builder.views.build(); + views = ImmutableMap.copyOf(builder.views); } public static Builder builder() @@ -46,9 +48,14 @@ public final class Views implements Iterable<ViewMetadata> return new Builder(); } + public Builder unbuild() + { + return builder().put(this); + } + public static Views none() { - return builder().build(); + return NONE; } public Iterator<ViewMetadata> iterator() @@ -56,7 +63,7 @@ public final class Views implements Iterable<ViewMetadata> return views.values().iterator(); } - public Iterable<TableMetadata> metadatas() + Iterable<TableMetadata> allTableMetadata() { return Iterables.transform(views.values(), view -> view.metadata); } @@ -71,9 +78,9 @@ public final class Views implements Iterable<ViewMetadata> return views.isEmpty(); } - public Iterable<ViewMetadata> forTable(UUID tableId) + public Iterable<ViewMetadata> forTable(TableId tableId) { - return Iterables.filter(this, v -> v.baseTableId.asUUID().equals(tableId)); + return Iterables.filter(this, v -> v.baseTableId.equals(tableId)); } /** @@ -99,20 +106,32 @@ public final class Views implements Iterable<ViewMetadata> return views.get(name); } + boolean containsView(String name) + { + return views.containsKey(name); + } + + Views filter(Predicate<ViewMetadata> predicate) + { + Builder builder = builder(); + views.values().stream().filter(predicate).forEach(builder::put); + return builder.build(); + } + /** * Create a MaterializedViews instance with the provided materialized view added */ public Views with(ViewMetadata view) { - if (get(view.name).isPresent()) - throw new IllegalStateException(String.format("Materialized View %s already exists", view.name)); + if (get(view.name()).isPresent()) + throw new IllegalStateException(String.format("Materialized View %s already exists", view.name())); - return builder().add(this).add(view).build(); + return builder().put(this).put(view).build(); } public Views withSwapped(ViewMetadata view) { - return without(view.name).with(view); + return without(view.name()).with(view); } /** @@ -123,18 +142,14 @@ public final class Views implements Iterable<ViewMetadata> ViewMetadata materializedView = get(name).orElseThrow(() -> new IllegalStateException(String.format("Materialized View %s doesn't exists", name))); - return builder().add(filter(this, v -> v != materializedView)).build(); + return filter(v -> v != materializedView); } - MapDifference<TableId, ViewMetadata> diff(Views other) + Views withUpdatedUserTypes(UserType udt) { - Map<TableId, ViewMetadata> thisViews = new HashMap<>(); - this.forEach(v -> thisViews.put(v.metadata.id, v)); - - Map<TableId, ViewMetadata> otherViews = new HashMap<>(); - other.forEach(v -> otherViews.put(v.metadata.id, v)); - - return Maps.difference(thisViews, otherViews); + return any(this, v -> v.referencesUserType(udt.name)) + ? builder().put(transform(this, v -> v.withUpdatedUserType(udt))).build() + : this; } @Override @@ -157,7 +172,7 @@ public final class Views implements Iterable<ViewMetadata> public static final class Builder { - final ImmutableMap.Builder<String, ViewMetadata> views = new ImmutableMap.Builder<>(); + final Map<String, ViewMetadata> views = new HashMap<>(); private Builder() { @@ -168,17 +183,61 @@ public final class Views implements Iterable<ViewMetadata> return new Views(this); } + public ViewMetadata get(String name) + { + return views.get(name); + } + + public Builder put(ViewMetadata view) + { + views.put(view.name(), view); + return this; + } - public Builder add(ViewMetadata view) + public Builder remove(String name) { - views.put(view.name, view); + views.remove(name); return this; } - public Builder add(Iterable<ViewMetadata> views) + public Builder put(Iterable<ViewMetadata> views) { - views.forEach(this::add); + views.forEach(this::put); return this; } } + + static ViewsDiff diff(Views before, Views after) + { + return ViewsDiff.diff(before, after); + } + + static final class ViewsDiff extends Diff<Views, ViewMetadata> + { + private static final ViewsDiff NONE = new ViewsDiff(Views.none(), Views.none(), ImmutableList.of()); + + private ViewsDiff(Views created, Views dropped, ImmutableCollection<Altered<ViewMetadata>> altered) + { + super(created, dropped, altered); + } + + private static ViewsDiff diff(Views before, Views after) + { + if (before == after) + return NONE; + + Views created = after.filter(v -> !before.containsView(v.name())); + Views dropped = before.filter(v -> !after.containsView(v.name())); + + ImmutableList.Builder<Altered<ViewMetadata>> altered = ImmutableList.builder(); + before.forEach(viewBefore -> + { + ViewMetadata viewAfter = after.getNullable(viewBefore.name()); + if (null != viewAfter) + viewBefore.compare(viewAfter).ifPresent(kind -> altered.add(new Altered<>(viewBefore, viewAfter, kind))); + }); + + return new ViewsDiff(created, dropped, altered.build()); + } + } } \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
