diff --git a/CHANGES.txt b/CHANGES.txt index c688615d10..b2132609a2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.0-rc1 + * Improve MV schema representation (CASSANDRA-9921) * Add flag to enable/disable coordinator batchlog for MV writes (CASSANDRA-10230) * Update cqlsh COPY for new internal driver serialization interface (CASSANDRA-10318) * Give index implementations more control over rebuild operations (CASSANDRA-10312) diff --git a/lib/cassandra-driver-core-3.0.0-alpha3-55db84c-SNAPSHOT-shaded.jar b/lib/cassandra-driver-core-3.0.0-alpha3-8bd064d-SNAPSHOT-shaded.jar similarity index 87% rename from lib/cassandra-driver-core-3.0.0-alpha3-55db84c-SNAPSHOT-shaded.jar rename to lib/cassandra-driver-core-3.0.0-alpha3-8bd064d-SNAPSHOT-shaded.jar index 385d947002..fc5d2f0c56 100644 Binary files a/lib/cassandra-driver-core-3.0.0-alpha3-55db84c-SNAPSHOT-shaded.jar and b/lib/cassandra-driver-core-3.0.0-alpha3-8bd064d-SNAPSHOT-shaded.jar differ diff --git a/lib/cassandra-driver-internal-only-3.0.0a2.post0-2429ba3.zip b/lib/cassandra-driver-internal-only-3.0.0a2.post0-96883eb.zip similarity index 78% rename from lib/cassandra-driver-internal-only-3.0.0a2.post0-2429ba3.zip rename to lib/cassandra-driver-internal-only-3.0.0a2.post0-96883eb.zip index 435c5e16dc..e55b4c327e 100644 Binary files a/lib/cassandra-driver-internal-only-3.0.0a2.post0-2429ba3.zip and b/lib/cassandra-driver-internal-only-3.0.0a2.post0-96883eb.zip differ diff --git a/src/java/org/apache/cassandra/concurrent/Stage.java b/src/java/org/apache/cassandra/concurrent/Stage.java index a57587c026..ccb156501e 100644 --- a/src/java/org/apache/cassandra/concurrent/Stage.java +++ b/src/java/org/apache/cassandra/concurrent/Stage.java @@ -27,7 +27,7 @@ public enum Stage READ, MUTATION, COUNTER_MUTATION, - MATERIALIZED_VIEW_MUTATION, + VIEW_MUTATION, GOSSIP, REQUEST_RESPONSE, ANTI_ENTROPY, @@ -61,7 +61,7 @@ public enum Stage return "internal"; case MUTATION: case COUNTER_MUTATION: - case MATERIALIZED_VIEW_MUTATION: + case VIEW_MUTATION: case READ: case REQUEST_RESPONSE: case READ_REPAIR: diff --git a/src/java/org/apache/cassandra/concurrent/StageManager.java b/src/java/org/apache/cassandra/concurrent/StageManager.java index ee1fbe5ccc..a2ad89240b 100644 --- a/src/java/org/apache/cassandra/concurrent/StageManager.java +++ b/src/java/org/apache/cassandra/concurrent/StageManager.java @@ -47,7 +47,7 @@ public class StageManager { stages.put(Stage.MUTATION, multiThreadedLowSignalStage(Stage.MUTATION, getConcurrentWriters())); stages.put(Stage.COUNTER_MUTATION, multiThreadedLowSignalStage(Stage.COUNTER_MUTATION, getConcurrentCounterWriters())); - stages.put(Stage.MATERIALIZED_VIEW_MUTATION, multiThreadedLowSignalStage(Stage.MATERIALIZED_VIEW_MUTATION, getConcurrentMaterializedViewWriters())); + stages.put(Stage.VIEW_MUTATION, multiThreadedLowSignalStage(Stage.VIEW_MUTATION, getConcurrentViewWriters())); stages.put(Stage.READ, multiThreadedLowSignalStage(Stage.READ, getConcurrentReaders())); stages.put(Stage.REQUEST_RESPONSE, multiThreadedLowSignalStage(Stage.REQUEST_RESPONSE, FBUtilities.getAvailableProcessors())); stages.put(Stage.INTERNAL_RESPONSE, multiThreadedStage(Stage.INTERNAL_RESPONSE, FBUtilities.getAvailableProcessors())); diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java index a26b19d686..cf1dbbf6f5 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -62,7 +62,7 @@ public final class CFMetaData { public enum Flag { - SUPER, COUNTER, DENSE, COMPOUND, VIEW + SUPER, COUNTER, DENSE, COMPOUND } private static final Logger logger = LoggerFactory.getLogger(CFMetaData.class); @@ -79,7 +79,7 @@ public final class CFMetaData private final boolean isCompound; private final boolean isSuper; private final boolean isCounter; - private final boolean isMaterializedView; + private final boolean isView; private final boolean isIndex; @@ -94,7 +94,6 @@ public final class CFMetaData private volatile AbstractType keyValidator = BytesType.instance; private volatile Map droppedColumns = new HashMap<>(); private volatile Triggers triggers = Triggers.none(); - private volatile MaterializedViews materializedViews = MaterializedViews.none(); private volatile Indexes indexes = Indexes.none(); /* @@ -219,12 +218,6 @@ public final class CFMetaData return this; } - public CFMetaData materializedViews(MaterializedViews prop) - { - materializedViews = prop; - return this; - } - public CFMetaData indexes(Indexes indexes) { this.indexes = indexes; @@ -238,7 +231,7 @@ public final class CFMetaData boolean isCounter, boolean isDense, boolean isCompound, - boolean isMaterializedView, + boolean isView, List partitionKeyColumns, List clusteringColumns, PartitionColumns partitionColumns, @@ -252,7 +245,7 @@ public final class CFMetaData this.isCompound = isCompound; this.isSuper = isSuper; this.isCounter = isCounter; - this.isMaterializedView = isMaterializedView; + this.isView = isView; EnumSet flags = EnumSet.noneOf(Flag.class); if (isSuper) @@ -263,8 +256,6 @@ public final class CFMetaData flags.add(Flag.DENSE); if (isCompound) flags.add(Flag.COMPOUND); - if (isMaterializedView) - flags.add(Flag.VIEW); this.flags = Sets.immutableEnumSet(flags); isIndex = cfName.contains("."); @@ -307,16 +298,6 @@ public final class CFMetaData this.compactValueColumn = CompactTables.getCompactValueColumn(partitionColumns, isSuper()); } - public MaterializedViews getMaterializedViews() - { - return materializedViews; - } - - public boolean hasMaterializedViews() - { - return !materializedViews.isEmpty(); - } - public Indexes getIndexes() { return indexes; @@ -329,7 +310,7 @@ public final class CFMetaData boolean isCompound, boolean isSuper, boolean isCounter, - boolean isMaterializedView, + boolean isView, List columns, IPartitioner partitioner) { @@ -363,7 +344,7 @@ public final class CFMetaData isCounter, isDense, isCompound, - isMaterializedView, + isView, partitions, clusterings, builder.build(), @@ -464,7 +445,7 @@ public final class CFMetaData isCounter(), isDense(), isCompound(), - isMaterializedView(), + isView(), copy(partitionKeyColumns), copy(clusteringColumns), copy(partitionColumns), @@ -481,7 +462,7 @@ public final class CFMetaData isCounter, isDense, isCompound, - isMaterializedView, + isView, copy(partitionKeyColumns), copy(clusteringColumns), copy(partitionColumns), @@ -511,7 +492,6 @@ public final class CFMetaData return newCFMD.params(oldCFMD.params) .droppedColumns(new HashMap<>(oldCFMD.droppedColumns)) .triggers(oldCFMD.triggers) - .materializedViews(oldCFMD.materializedViews) .indexes(oldCFMD.indexes); } @@ -702,7 +682,6 @@ public final class CFMetaData && Objects.equal(columnMetadata, other.columnMetadata) && Objects.equal(droppedColumns, other.droppedColumns) && Objects.equal(triggers, other.triggers) - && Objects.equal(materializedViews, other.materializedViews) && Objects.equal(indexes, other.indexes); } @@ -720,7 +699,6 @@ public final class CFMetaData .append(columnMetadata) .append(droppedColumns) .append(triggers) - .append(materializedViews) .append(indexes) .toHashCode(); } @@ -731,7 +709,8 @@ public final class CFMetaData */ public boolean reload() { - return apply(SchemaKeyspace.createTableFromName(ksName, cfName)); + return apply(isView ? SchemaKeyspace.createViewFromName(ksName, cfName).metadata + : SchemaKeyspace.createTableFromName(ksName, cfName)); } /** @@ -766,7 +745,6 @@ public final class CFMetaData droppedColumns = cfm.droppedColumns; triggers = cfm.triggers; - materializedViews = cfm.materializedViews; indexes = cfm.indexes; logger.debug("application result is {}", this); @@ -1070,9 +1048,9 @@ public final class CFMetaData return isCompound; } - public boolean isMaterializedView() + public boolean isView() { - return isMaterializedView; + return isView; } public Serializers serializers() @@ -1121,7 +1099,6 @@ public final class CFMetaData .append("columnMetadata", columnMetadata.values()) .append("droppedColumns", droppedColumns) .append("triggers", triggers) - .append("materializedViews", materializedViews) .append("indexes", indexes) .toString(); } @@ -1134,7 +1111,7 @@ public final class CFMetaData private final boolean isCompound; private final boolean isSuper; private final boolean isCounter; - private final boolean isMaterializedView; + private final boolean isView; private IPartitioner partitioner; private UUID tableId; @@ -1144,7 +1121,7 @@ public final class CFMetaData private final List> staticColumns = new ArrayList<>(); private final List> regularColumns = new ArrayList<>(); - private Builder(String keyspace, String table, boolean isDense, boolean isCompound, boolean isSuper, boolean isCounter, boolean isMaterializedView) + private Builder(String keyspace, String table, boolean isDense, boolean isCompound, boolean isSuper, boolean isCounter, boolean isView) { this.keyspace = keyspace; this.table = table; @@ -1152,7 +1129,7 @@ public final class CFMetaData this.isCompound = isCompound; this.isSuper = isSuper; this.isCounter = isCounter; - this.isMaterializedView = isMaterializedView; + this.isView = isView; this.partitioner = DatabaseDescriptor.getPartitioner(); } @@ -1296,7 +1273,7 @@ public final class CFMetaData isCounter, isDense, isCompound, - isMaterializedView, + isView, partitions, clusterings, builder.build(), diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index ed220b8677..7553c920d3 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -1143,7 +1143,7 @@ public class DatabaseDescriptor return conf.concurrent_counter_writes; } - public static int getConcurrentMaterializedViewWriters() + public static int getConcurrentViewWriters() { return conf.concurrent_materialized_view_writes; } diff --git a/src/java/org/apache/cassandra/config/MaterializedViewDefinition.java b/src/java/org/apache/cassandra/config/MaterializedViewDefinition.java deleted file mode 100644 index 90fa35c1bf..0000000000 --- a/src/java/org/apache/cassandra/config/MaterializedViewDefinition.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.cassandra.config; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.apache.cassandra.cql3.ColumnIdentifier; - -public class MaterializedViewDefinition -{ - public final String baseCfName; - public final String viewName; - // The order of partititon columns and clustering columns is important, so we cannot switch these two to sets - public final List partitionColumns; - public final List clusteringColumns; - public final Set included; - public final boolean includeAll; - - public MaterializedViewDefinition(MaterializedViewDefinition def) - { - this(def.baseCfName, def.viewName, new ArrayList<>(def.partitionColumns), new ArrayList<>(def.clusteringColumns), new HashSet<>(def.included)); - } - - /** - * @param baseCfName Name of the column family from which this view is based - * @param viewName Name of the view - * @param partitionColumns List of all of the partition columns, in the order they are defined - * @param clusteringColumns List of all of the clustering columns, in the order they are defined - * @param included - */ - public MaterializedViewDefinition(String baseCfName, String viewName, List partitionColumns, List clusteringColumns, Set included) - { - assert partitionColumns != null && !partitionColumns.isEmpty(); - assert included != null; - this.baseCfName = baseCfName; - this.viewName = viewName; - this.partitionColumns = partitionColumns; - this.clusteringColumns = clusteringColumns; - this.includeAll = included.isEmpty(); - this.included = included; - } - - /** - * @return true if the view specified by this definition will include the column, false otherwise - */ - public boolean includes(ColumnIdentifier column) - { - return includeAll - || partitionColumns.contains(column) - || clusteringColumns.contains(column) - || included.contains(column); - } - - /** - * Replace the column {@param from} with {@param to} in this materialized view definition's partition, - * clustering, or included columns. - */ - public void renameColumn(ColumnIdentifier from, ColumnIdentifier to) - { - if (!includeAll && included.contains(from)) - { - included.remove(from); - included.add(to); - } - - int partitionIndex = partitionColumns.indexOf(from); - if (partitionIndex >= 0) - partitionColumns.set(partitionIndex, to); - - int clusteringIndex = clusteringColumns.indexOf(from); - if (clusteringIndex >= 0) - clusteringColumns.set(clusteringIndex, to); - } -} diff --git a/src/java/org/apache/cassandra/config/Schema.java b/src/java/org/apache/cassandra/config/Schema.java index 7cc8394d35..bcde978479 100644 --- a/src/java/org/apache/cassandra/config/Schema.java +++ b/src/java/org/apache/cassandra/config/Schema.java @@ -147,6 +147,7 @@ public class Schema public Schema load(KeyspaceMetadata keyspaceDef) { keyspaceDef.tables.forEach(this::load); + keyspaceDef.views.forEach(this::load); setKeyspaceMetadata(keyspaceDef); return this; } @@ -224,8 +225,11 @@ public class Schema public CFMetaData getCFMetaData(String keyspaceName, String cfName) { assert keyspaceName != null; + KeyspaceMetadata ksm = keyspaces.get(keyspaceName); - return (ksm == null) ? null : ksm.tables.getNullable(cfName); + return ksm == null + ? null + : ksm.getTableOrViewNullable(cfName); } /** @@ -246,6 +250,13 @@ public class Schema return getCFMetaData(descriptor.ksname, descriptor.cfname); } + public ViewDefinition getView(String keyspaceName, String viewName) + { + assert keyspaceName != null; + KeyspaceMetadata ksm = keyspaces.get(keyspaceName); + return (ksm == null) ? null : ksm.views.getNullable(viewName); + } + /** * Get metadata about keyspace by its name * @@ -274,12 +285,12 @@ public class Schema * * @return metadata about ColumnFamilies the belong to the given keyspace */ - public Tables getTables(String keyspaceName) + public Iterable getTablesAndViews(String keyspaceName) { assert keyspaceName != null; KeyspaceMetadata ksm = keyspaces.get(keyspaceName); assert ksm != null; - return ksm.tables; + return ksm.tablesAndViews(); } /** @@ -355,6 +366,24 @@ public class Schema cfIdMap.put(key, cfm.cfId); } + /** + * Load individual View Definition to the schema + * (to make View lookup faster) + * + * @param view The View definition to load + */ + public void load(ViewDefinition view) + { + CFMetaData cfm = view.metadata; + Pair key = Pair.create(cfm.ksName, cfm.cfName); + + if (cfIdMap.containsKey(key)) + throw new RuntimeException(String.format("Attempting to load already loaded view %s.%s", cfm.ksName, cfm.cfName)); + + logger.debug("Adding {} to cfIdMap", cfm); + cfIdMap.put(key, cfm.cfId); + } + /** * Used for ColumnFamily data eviction out from the schema * @@ -365,6 +394,16 @@ public class Schema cfIdMap.remove(Pair.create(cfm.ksName, cfm.cfName)); } + /** + * Used for View eviction from the schema + * + * @param view The view definition to evict + */ + private void unload(ViewDefinition view) + { + cfIdMap.remove(Pair.create(view.ksName, view.viewName)); + } + /* Function helpers */ /** @@ -442,6 +481,7 @@ public class Schema { KeyspaceMetadata ksm = getKSMetaData(keyspaceName); ksm.tables.forEach(this::unload); + ksm.views.forEach(this::unload); clearKeyspaceMetadata(ksm); } @@ -468,13 +508,13 @@ public class Schema KeyspaceMetadata ksm = Schema.instance.getKSMetaData(ksName); String snapshotName = Keyspace.getTimestampedSnapshotName(ksName); - CompactionManager.instance.interruptCompactionFor(ksm.tables, true); + CompactionManager.instance.interruptCompactionFor(ksm.tablesAndViews(), true); Keyspace keyspace = Keyspace.open(ksm.name); // remove all cfs from the keyspace instance. List droppedCfs = new ArrayList<>(); - for (CFMetaData cfm : ksm.tables) + for (CFMetaData cfm : ksm.tablesAndViews()) { ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfm.cfName); @@ -556,6 +596,67 @@ public class Schema CommitLog.instance.forceRecycleAllSegments(Collections.singleton(cfm.cfId)); } + public void addView(ViewDefinition view) + { + assert getCFMetaData(view.ksName, view.viewName) == null; + + update(view.ksName, ks -> + { + load(view); + + // make sure it's init-ed w/ the old definitions first, + // since we're going to call initCf on the new one manually + Keyspace.open(view.ksName); + + return ks.withSwapped(ks.views.with(view)); + }); + + Keyspace.open(view.ksName).initCf(view.metadata.cfId, view.viewName, true); + Keyspace.open(view.ksName).viewManager.reload(); + MigrationManager.instance.notifyCreateView(view); + } + + public void updateView(String ksName, String viewName) + { + Optional optView = getKSMetaData(ksName).views.get(viewName); + assert optView.isPresent(); + ViewDefinition view = optView.get(); + boolean columnsDidChange = view.metadata.reload(); + + Keyspace keyspace = Keyspace.open(view.ksName); + keyspace.getColumnFamilyStore(view.viewName).reload(); + Keyspace.open(view.ksName).viewManager.update(view.viewName); + MigrationManager.instance.notifyUpdateView(view, columnsDidChange); + } + + public void dropView(String ksName, String viewName) + { + KeyspaceMetadata oldKsm = getKSMetaData(ksName); + assert oldKsm != null; + ColumnFamilyStore cfs = Keyspace.open(ksName).getColumnFamilyStore(viewName); + assert cfs != null; + + // make sure all the indexes are dropped, or else. + cfs.indexManager.markAllIndexesRemoved(); + + // reinitialize the keyspace. + ViewDefinition view = oldKsm.views.get(viewName).get(); + KeyspaceMetadata newKsm = oldKsm.withSwapped(oldKsm.views.without(viewName)); + + unload(view); + setKeyspaceMetadata(newKsm); + + CompactionManager.instance.interruptCompactionFor(Collections.singleton(view.metadata), true); + + if (DatabaseDescriptor.isAutoSnapshot()) + cfs.snapshot(Keyspace.getTimestampedSnapshotName(cfs.name)); + Keyspace.open(ksName).dropCf(view.metadata.cfId); + Keyspace.open(ksName).viewManager.reload(); + MigrationManager.instance.notifyDropView(view); + + CommitLog.instance.forceRecycleAllSegments(Collections.singleton(view.metadata.cfId)); + } + public void addType(UserType ut) { update(ut.keyspace, ks -> ks.withSwapped(ks.types.with(ut))); diff --git a/src/java/org/apache/cassandra/config/ViewDefinition.java b/src/java/org/apache/cassandra/config/ViewDefinition.java new file mode 100644 index 0000000000..39695b98ce --- /dev/null +++ b/src/java/org/apache/cassandra/config/ViewDefinition.java @@ -0,0 +1,114 @@ +/* + * 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.config; + +import java.util.Objects; +import java.util.UUID; + +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import org.apache.cassandra.cql3.ColumnIdentifier; + +public class ViewDefinition +{ + public final String ksName; + public final String viewName; + public final UUID baseTableId; + public final boolean includeAllColumns; + // The order of partititon columns and clustering columns is important, so we cannot switch these two to sets + public final CFMetaData metadata; + + public ViewDefinition(ViewDefinition def) + { + this(def.ksName, def.viewName, def.baseTableId, def.includeAllColumns, def.metadata); + } + + /** + * @param viewName 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 ViewDefinition(String ksName, String viewName, UUID baseTableId, boolean includeAllColumns, CFMetaData metadata) + { + this.ksName = ksName; + this.viewName = viewName; + this.baseTableId = baseTableId; + this.includeAllColumns = includeAllColumns; + this.metadata = metadata; + } + + /** + * @return true if the view specified by this definition will include the column, false otherwise + */ + public boolean includes(ColumnIdentifier column) + { + return metadata.getColumnDefinition(column) != null; + } + + public ViewDefinition copy() + { + return new ViewDefinition(ksName, viewName, baseTableId, includeAllColumns, metadata.copy()); + } + + public CFMetaData baseTableMetadata() + { + return Schema.instance.getCFMetaData(baseTableId); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof ViewDefinition)) + return false; + + ViewDefinition other = (ViewDefinition) o; + return Objects.equals(ksName, other.ksName) + && Objects.equals(viewName, other.viewName) + && Objects.equals(baseTableId, other.baseTableId) + && Objects.equals(includeAllColumns, other.includeAllColumns) + && Objects.equals(metadata, other.metadata); + } + + @Override + public int hashCode() + { + return new HashCodeBuilder(29, 1597) + .append(ksName) + .append(viewName) + .append(baseTableId) + .append(includeAllColumns) + .append(metadata) + .toHashCode(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this) + .append("ksName", ksName) + .append("viewName", viewName) + .append("baseTableId", baseTableId) + .append("includeAllColumns", includeAllColumns) + .append("metadata", metadata) + .toString(); + } +} diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index 87bec4bc1f..f6d54f5359 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -740,7 +740,7 @@ indexIdent returns [IndexTarget.Raw id] * PRIMARY KEY () * WITH = AND ...; */ -createMaterializedViewStatement returns [CreateMaterializedViewStatement expr] +createMaterializedViewStatement returns [CreateViewStatement expr] @init { boolean ifNotExists = false; List partitionKeys = new ArrayList<>(); @@ -753,7 +753,7 @@ createMaterializedViewStatement returns [CreateMaterializedViewStatement expr] '(' '(' k1=cident { partitionKeys.add(k1); } ( ',' kn=cident { partitionKeys.add(kn); } )* ')' ( ',' c1=cident { compositeKeys.add(c1); } )* ')' | '(' k1=cident { partitionKeys.add(k1); } ( ',' cn=cident { compositeKeys.add(cn); } )* ')' ) - { $expr = new CreateMaterializedViewStatement(cf, basecf, sclause, wclause, partitionKeys, compositeKeys, ifNotExists); } + { $expr = new CreateViewStatement(cf, basecf, sclause, wclause, partitionKeys, compositeKeys, ifNotExists); } ( K_WITH cfamProperty[expr.properties] ( K_AND cfamProperty[expr.properties] )*)? ; @@ -820,14 +820,14 @@ alterTableStatement returns [AlterTableStatement expr] } ; -alterMaterializedViewStatement returns [AlterMaterializedViewStatement expr] +alterMaterializedViewStatement returns [AlterViewStatement expr] @init { TableAttributes attrs = new TableAttributes(); } : K_ALTER K_MATERIALIZED K_VIEW name=columnFamilyName K_WITH properties[attrs] { - $expr = new AlterMaterializedViewStatement(name, attrs); + $expr = new AlterViewStatement(name, attrs); } ; @@ -886,10 +886,10 @@ dropIndexStatement returns [DropIndexStatement expr] /** * DROP MATERIALIZED VIEW [IF EXISTS] */ -dropMaterializedViewStatement returns [DropMaterializedViewStatement expr] +dropMaterializedViewStatement returns [DropViewStatement expr] @init { boolean ifExists = false; } : K_DROP K_MATERIALIZED K_VIEW (K_IF K_EXISTS { ifExists = true; } )? cf=columnFamilyName - { $expr = new DropMaterializedViewStatement(cf, ifExists); } + { $expr = new DropViewStatement(cf, ifExists); } ; /** diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java index fac0c5318a..af9a75c5d7 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@ -19,6 +19,8 @@ package org.apache.cassandra.cql3.statements; import java.util.*; +import com.google.common.collect.Iterables; + import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.CFName; @@ -27,6 +29,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.CounterColumnType; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.schema.IndexMetadata; import org.apache.cassandra.schema.Indexes; @@ -81,7 +84,7 @@ public class AlterTableStatement extends SchemaAlteringStatement public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException { CFMetaData meta = validateColumnFamily(keyspace(), columnFamily()); - if (meta.isMaterializedView()) + if (meta.isView()) throw new InvalidRequestException("Cannot use ALTER TABLE on Materialized View"); CFMetaData cfm = meta.copy(); @@ -95,7 +98,8 @@ public class AlterTableStatement extends SchemaAlteringStatement def = cfm.getColumnDefinition(columnName); } - List materializedViewUpdates = null; + List viewUpdates = null; + Iterable views = View.findAll(keyspace(), columnFamily()); switch (oType) { @@ -156,19 +160,20 @@ public class AlterTableStatement extends SchemaAlteringStatement ? ColumnDefinition.staticDef(cfm, columnName.bytes, type) : ColumnDefinition.regularDef(cfm, columnName.bytes, type)); - // Adding a column to a table which has an include all materialized view requires the column to be added - // to the materialized view as well - for (MaterializedViewDefinition mv : cfm.getMaterializedViews()) + // Adding a column to a table which has an include all view requires the column to be added to the view + // as well + if (!isStatic) { - if (mv.includeAll) + for (ViewDefinition view : views) { - CFMetaData indexCfm = Schema.instance.getCFMetaData(keyspace(), mv.viewName).copy(); - indexCfm.addColumnDefinition(isStatic - ? ColumnDefinition.staticDef(indexCfm, columnName.bytes, type) - : ColumnDefinition.regularDef(indexCfm, columnName.bytes, type)); - if (materializedViewUpdates == null) - materializedViewUpdates = new ArrayList<>(); - materializedViewUpdates.add(indexCfm); + if (view.includeAllColumns) + { + ViewDefinition viewCopy = view.copy(); + viewCopy.metadata.addColumnDefinition(ColumnDefinition.regularDef(viewCopy.metadata, columnName.bytes, type)); + if (viewUpdates == null) + viewUpdates = new ArrayList<>(); + viewUpdates.add(viewCopy); + } } } break; @@ -221,17 +226,16 @@ public class AlterTableStatement extends SchemaAlteringStatement // In any case, we update the column definition cfm.addOrReplaceColumnDefinition(def.withNewType(validatorType)); - // We have to alter the schema of the materialized view table as well; it doesn't affect the definition however - for (MaterializedViewDefinition mv : cfm.getMaterializedViews()) + // We have to alter the schema of the view table as well; it doesn't affect the definition however + for (ViewDefinition view : views) { - if (!mv.includes(columnName)) continue; - // We have to use the pre-adjusted CFM, otherwise we can't resolve the Index - CFMetaData indexCfm = Schema.instance.getCFMetaData(keyspace(), mv.viewName).copy(); - indexCfm.addOrReplaceColumnDefinition(def.withNewType(validatorType)); + if (!view.includes(columnName)) continue; + ViewDefinition viewCopy = view.copy(); + viewCopy.metadata.addOrReplaceColumnDefinition(def.withNewType(validatorType)); - if (materializedViewUpdates == null) - materializedViewUpdates = new ArrayList<>(); - materializedViewUpdates.add(indexCfm); + if (viewUpdates == null) + viewUpdates = new ArrayList<>(); + viewUpdates.add(viewCopy); } break; @@ -276,20 +280,16 @@ public class AlterTableStatement extends SchemaAlteringStatement } cfm.indexes(allIndexes); - // If a column is dropped which is the target of a materialized view, - // then we need to drop the view. - // If a column is dropped which was selected into a materialized view, - // we need to drop that column from the included materialzied view table - // and definition. + // If a column is dropped which is included in a view, we don't allow the drop to take place. boolean rejectAlter = false; StringBuilder builder = new StringBuilder(); - for (MaterializedViewDefinition mv : cfm.getMaterializedViews()) + for (ViewDefinition view : views) { - if (!mv.includes(columnName)) continue; + if (!view.includes(columnName)) continue; if (rejectAlter) builder.append(','); rejectAlter = true; - builder.append(mv.viewName); + builder.append(view.viewName); } if (rejectAlter) throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})", @@ -304,7 +304,7 @@ public class AlterTableStatement extends SchemaAlteringStatement TableParams params = attrs.asAlteredTableParams(cfm.params); - if (cfm.hasMaterializedViews() && params.gcGraceSeconds == 0) + if (!Iterables.isEmpty(views) && params.gcGraceSeconds == 0) { throw new InvalidRequestException("Cannot alter gc_grace_seconds of the base table of a " + "materialized view to 0, since this value is used to TTL " + @@ -326,36 +326,31 @@ public class AlterTableStatement extends SchemaAlteringStatement ColumnIdentifier to = entry.getValue().prepare(cfm); cfm.renameColumn(from, to); - // If the materialized view includes a renamed column, it must be renamed in the index table and the definition. - for (MaterializedViewDefinition mv : cfm.getMaterializedViews()) + // If the view includes a renamed column, it must be renamed in the view table and the definition. + for (ViewDefinition view : views) { - if (!mv.includes(from)) continue; + if (!view.includes(from)) continue; - CFMetaData indexCfm = Schema.instance.getCFMetaData(keyspace(), mv.viewName).copy(); - ColumnIdentifier indexFrom = entry.getKey().prepare(indexCfm); - ColumnIdentifier indexTo = entry.getValue().prepare(indexCfm); - indexCfm.renameColumn(indexFrom, indexTo); + ViewDefinition viewCopy = view.copy(); + ColumnIdentifier viewFrom = entry.getKey().prepare(viewCopy.metadata); + ColumnIdentifier viewTo = entry.getValue().prepare(viewCopy.metadata); + viewCopy.metadata.renameColumn(viewFrom, viewTo); - MaterializedViewDefinition mvCopy = new MaterializedViewDefinition(mv); - mvCopy.renameColumn(from, to); - - cfm.materializedViews(cfm.getMaterializedViews().replace(mvCopy)); - - if (materializedViewUpdates == null) - materializedViewUpdates = new ArrayList<>(); - materializedViewUpdates.add(indexCfm); + if (viewUpdates == null) + viewUpdates = new ArrayList<>(); + viewUpdates.add(viewCopy); } } break; } - if (materializedViewUpdates != null) - { - for (CFMetaData mvUpdates : materializedViewUpdates) - MigrationManager.announceColumnFamilyUpdate(mvUpdates, false, isLocalOnly); - } - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + + if (viewUpdates != null) + { + for (ViewDefinition viewUpdate : viewUpdates) + MigrationManager.announceViewUpdate(viewUpdate, isLocalOnly); + } return true; } diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java index 4298c224eb..e176cec7ee 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java @@ -115,6 +115,16 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement MigrationManager.announceColumnFamilyUpdate(copy, false, 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.types) { diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterMaterializedViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java similarity index 79% rename from src/java/org/apache/cassandra/cql3/statements/AlterMaterializedViewStatement.java rename to src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java index bc4ba11174..e578c4fee1 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterMaterializedViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java @@ -19,8 +19,10 @@ package org.apache.cassandra.cql3.statements; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.db.view.MaterializedView; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; @@ -31,11 +33,11 @@ import org.apache.cassandra.transport.Event; import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily; -public class AlterMaterializedViewStatement extends SchemaAlteringStatement +public class AlterViewStatement extends SchemaAlteringStatement { private final TableAttributes attrs; - public AlterMaterializedViewStatement(CFName name, TableAttributes attrs) + public AlterViewStatement(CFName name, TableAttributes attrs) { super(name); this.attrs = attrs; @@ -43,7 +45,7 @@ public class AlterMaterializedViewStatement extends SchemaAlteringStatement public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException { - CFMetaData baseTable = MaterializedView.findBaseTable(keyspace(), columnFamily()); + CFMetaData baseTable = View.findBaseTable(keyspace(), columnFamily()); if (baseTable != null) state.hasColumnFamilyAccess(keyspace(), baseTable.cfName, Permission.ALTER); } @@ -56,32 +58,33 @@ public class AlterMaterializedViewStatement extends SchemaAlteringStatement public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException { CFMetaData meta = validateColumnFamily(keyspace(), columnFamily()); - if (!meta.isMaterializedView()) + if (!meta.isView()) throw new InvalidRequestException("Cannot use ALTER MATERIALIZED VIEW on Table"); - CFMetaData cfm = meta.copy(); + ViewDefinition view = Schema.instance.getView(keyspace(), columnFamily()); + ViewDefinition viewCopy = view.copy(); if (attrs == null) throw new InvalidRequestException("ALTER MATERIALIZED VIEW WITH invoked, but no parameters found"); attrs.validate(); - TableParams params = attrs.asAlteredTableParams(cfm.params); + TableParams params = attrs.asAlteredTableParams(view.metadata.params); if (params.gcGraceSeconds == 0) { throw new InvalidRequestException("Cannot alter gc_grace_seconds of a materialized view to 0, since this " + "value is used to TTL undelivered updates. Setting gc_grace_seconds too " + "low might cause undelivered updates to expire before being replayed."); } - cfm.params(params); + view.metadata.params(params); - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + MigrationManager.announceViewUpdate(viewCopy, isLocalOnly); return true; } public String toString() { - return String.format("AlterMaterializedViewStatement(name=%s)", cfName); + return String.format("AlterViewStatement(name=%s)", cfName); } public Event.SchemaChange changeEvent() diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java index 706cfeac1f..2589622090 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java @@ -59,7 +59,7 @@ public class CreateTriggerStatement extends SchemaAlteringStatement public void validate(ClientState state) throws RequestValidationException { CFMetaData cfm = ThriftValidation.validateColumnFamily(keyspace(), columnFamily()); - if (cfm.isMaterializedView()) + if (cfm.isView()) throw new InvalidRequestException("Cannot CREATE TRIGGER against a materialized view"); try diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateMaterializedViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java similarity index 78% rename from src/java/org/apache/cassandra/cql3/statements/CreateMaterializedViewStatement.java rename to src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java index dee05774a2..1a020ce16d 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateMaterializedViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java @@ -28,23 +28,27 @@ import com.google.common.collect.Iterables; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.config.MaterializedViewDefinition; +import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.cql3.CFName; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.selection.RawSelector; import org.apache.cassandra.cql3.selection.Selectable; -import org.apache.cassandra.db.view.MaterializedView; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.ReversedType; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.AlreadyExistsException; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; +import org.apache.cassandra.schema.TableParams; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.ClientWarn; import org.apache.cassandra.service.MigrationManager; import org.apache.cassandra.thrift.ThriftValidation; import org.apache.cassandra.transport.Event; -public class CreateMaterializedViewStatement extends SchemaAlteringStatement +public class CreateViewStatement extends SchemaAlteringStatement { private final CFName baseName; private final List selectClause; @@ -54,13 +58,13 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement public final CFProperties properties = new CFProperties(); private final boolean ifNotExists; - public CreateMaterializedViewStatement(CFName viewName, - CFName baseName, - List selectClause, - List notNullWhereClause, - List partitionKeys, - List clusteringKeys, - boolean ifNotExists) + public CreateViewStatement(CFName viewName, + CFName baseName, + List selectClause, + List notNullWhereClause, + List partitionKeys, + List clusteringKeys, + boolean ifNotExists) { super(viewName); this.baseName = baseName; @@ -84,6 +88,31 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement // We do validation in announceMigration to reduce doubling up of work } + private interface AddColumn { + void add(ColumnIdentifier identifier, AbstractType type); + } + + private void add(CFMetaData baseCfm, Iterable columns, AddColumn adder) + { + for (ColumnIdentifier column : columns) + { + AbstractType type = baseCfm.getColumnDefinition(column).type; + if (properties.definedOrdering.containsKey(column)) + { + boolean desc = properties.definedOrdering.get(column); + if (!desc && type.isReversed()) + { + type = ((ReversedType)type).baseType; + } + else if (desc && !type.isReversed()) + { + type = ReversedType.getInstance(type); + } + } + adder.add(column, type); + } + } + public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException { // We need to make sure that: @@ -109,8 +138,7 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement if (cfm.isCounter()) throw new InvalidRequestException("Materialized views are not supported on counter tables"); - - if (cfm.isMaterializedView()) + if (cfm.isView()) throw new InvalidRequestException("Materialized views cannot be created against other materialized views"); if (cfm.params.gcGraceSeconds == 0) @@ -194,18 +222,25 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement } // We need to include all of the primary key colums from the base table in order to make sure that we do not - // overwrite values in the materialized view. We cannot support "collapsing" the base table into a smaller - // number of rows in the view because if we need to generate a tombstone, we have no way of knowing which value - // is currently being used in the view and whether or not to generate a tombstone. - // In order to not surprise our users, we require that they include all of the columns. We provide them with - // a list of all of the columns left to include. + // overwrite values in the view. We cannot support "collapsing" the base table into a smaller number of rows in + // the view because if we need to generate a tombstone, we have no way of knowing which value is currently being + // used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require + // that they include all of the columns. We provide them with a list of all of the columns left to include. boolean missingClusteringColumns = false; StringBuilder columnNames = new StringBuilder(); + List includedColumns = new ArrayList<>(); for (ColumnDefinition def : cfm.allColumns()) { + ColumnIdentifier identifier = def.name; + + if ((included.isEmpty() || included.contains(identifier)) + && !targetClusteringColumns.contains(identifier) && !targetPartitionKeys.contains(identifier) + && !def.isStatic()) + { + includedColumns.add(identifier); + } if (!def.isPrimaryKeyColumn()) continue; - ColumnIdentifier identifier = def.name; if (!targetClusteringColumns.contains(identifier) && !targetPartitionKeys.contains(identifier)) { if (missingClusteringColumns) @@ -225,16 +260,21 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement if (targetClusteringColumns.isEmpty()) throw new InvalidRequestException("No columns are defined for Materialized View other than primary key"); - MaterializedViewDefinition definition = new MaterializedViewDefinition(baseName.getColumnFamily(), - columnFamily(), - targetPartitionKeys, - targetClusteringColumns, - included); + CFMetaData.Builder cfmBuilder = CFMetaData.Builder.createView(keyspace(), columnFamily()); + add(cfm, targetPartitionKeys, cfmBuilder::addPartitionKey); + add(cfm, targetClusteringColumns, cfmBuilder::addClusteringColumn); + add(cfm, includedColumns, cfmBuilder::addRegularColumn); + TableParams params = properties.properties.asNewTableParams(); + CFMetaData viewCfm = cfmBuilder.build().params(params); + ViewDefinition definition = new ViewDefinition(keyspace(), + columnFamily(), + Schema.instance.getId(keyspace(), baseName.getColumnFamily()), + included.isEmpty(), + viewCfm); - CFMetaData indexCf = MaterializedView.getCFMetaData(definition, cfm, properties); try { - MigrationManager.announceNewColumnFamily(indexCf, isLocalOnly); + MigrationManager.announceNewView(definition, isLocalOnly); } catch (AlreadyExistsException e) { @@ -243,11 +283,6 @@ public class CreateMaterializedViewStatement extends SchemaAlteringStatement throw e; } - CFMetaData newCfm = cfm.copy(); - newCfm.materializedViews(newCfm.getMaterializedViews().with(definition)); - - MigrationManager.announceColumnFamilyUpdate(newCfm, false, isLocalOnly); - return true; } diff --git a/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java index 35dc94728d..14d89d92bd 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java @@ -19,12 +19,13 @@ package org.apache.cassandra.cql3.statements; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; -import org.apache.cassandra.config.MaterializedViewDefinition; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.config.Schema; import org.apache.cassandra.cql3.CFName; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.UnauthorizedException; +import org.apache.cassandra.schema.KeyspaceMetadata; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.MigrationManager; import org.apache.cassandra.transport.Event; @@ -61,20 +62,24 @@ public class DropTableStatement extends SchemaAlteringStatement { try { - CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()); + KeyspaceMetadata ksm = Schema.instance.getKSMetaData(keyspace()); + CFMetaData cfm = ksm.tables.getNullable(columnFamily()); if (cfm != null) { - if (cfm.isMaterializedView()) + if (cfm.isView()) throw new InvalidRequestException("Cannot use DROP TABLE on Materialized View"); boolean rejectDrop = false; StringBuilder messageBuilder = new StringBuilder(); - for (MaterializedViewDefinition def : cfm.getMaterializedViews()) + for (ViewDefinition def : ksm.views) { - if (rejectDrop) - messageBuilder.append(','); - rejectDrop = true; - messageBuilder.append(def.viewName); + if (def.baseTableId.equals(cfm.cfId)) + { + if (rejectDrop) + messageBuilder.append(','); + rejectDrop = true; + messageBuilder.append(def.viewName); + } } if (rejectDrop) { diff --git a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java index 75f6200e59..74c8c36176 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java @@ -87,7 +87,7 @@ public class DropTypeStatement extends SchemaAlteringStatement if (!ut.name.equals(name.getUserTypeName()) && isUsedBy(ut)) throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by user type %s", name, ut.asCQL3Type())); - for (CFMetaData cfm : ksm.tables) + for (CFMetaData cfm : ksm.tablesAndViews()) for (ColumnDefinition def : cfm.allColumns()) if (isUsedBy(def.type)) throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by table %s.%s", name, cfm.ksName, cfm.cfName)); diff --git a/src/java/org/apache/cassandra/cql3/statements/DropMaterializedViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropViewStatement.java similarity index 58% rename from src/java/org/apache/cassandra/cql3/statements/DropMaterializedViewStatement.java rename to src/java/org/apache/cassandra/cql3/statements/DropViewStatement.java index 8adba45019..f2be370c7f 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DropMaterializedViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropViewStatement.java @@ -21,8 +21,9 @@ package org.apache.cassandra.cql3.statements; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.db.view.MaterializedView; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.UnauthorizedException; @@ -30,11 +31,11 @@ import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.MigrationManager; import org.apache.cassandra.transport.Event; -public class DropMaterializedViewStatement extends SchemaAlteringStatement +public class DropViewStatement extends SchemaAlteringStatement { public final boolean ifExists; - public DropMaterializedViewStatement(CFName cf, boolean ifExists) + public DropViewStatement(CFName cf, boolean ifExists) { super(cf); this.ifExists = ifExists; @@ -42,7 +43,7 @@ public class DropMaterializedViewStatement extends SchemaAlteringStatement public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException { - CFMetaData baseTable = MaterializedView.findBaseTable(keyspace(), columnFamily()); + CFMetaData baseTable = View.findBaseTable(keyspace(), columnFamily()); if (baseTable != null) state.hasColumnFamilyAccess(keyspace(), baseTable.cfName, Permission.ALTER); } @@ -61,25 +62,25 @@ public class DropMaterializedViewStatement extends SchemaAlteringStatement { try { - CFMetaData viewCfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()); - if (viewCfm == null) - throw new ConfigurationException(String.format("Cannot drop non existing materialized view '%s' in keyspace '%s'.", columnFamily(), keyspace())); - if (!viewCfm.isMaterializedView()) - throw new ConfigurationException(String.format("Cannot drop non materialized view '%s' in keyspace '%s'", columnFamily(), keyspace())); +// ViewDefinition view = Schema.instance.getViewDefinition(keyspace(), columnFamily()); +// if (view == null) +// { +// if (Schema.instance.getCFMetaData(keyspace(), columnFamily()) != null) +// throw new ConfigurationException(String.format("Cannot drop table '%s' in keyspace '%s'.", columnFamily(), keyspace())); +// +// throw new ConfigurationException(String.format("Cannot drop non existing materialized view '%s' in keyspace '%s'.", columnFamily(), keyspace())); +// } +// +// CFMetaData baseCfm = Schema.instance.getCFMetaData(view.baseTableId); +// if (baseCfm == null) +// { +// if (ifExists) +// throw new ConfigurationException(String.format("Cannot drop materialized view '%s' in keyspace '%s' without base CF.", columnFamily(), keyspace())); +// else +// throw new InvalidRequestException(String.format("View '%s' could not be found in any of the tables of keyspace '%s'", cfName, keyspace())); +// } - CFMetaData baseCfm = MaterializedView.findBaseTable(keyspace(), columnFamily()); - if (baseCfm == null) - { - if (ifExists) - throw new ConfigurationException(String.format("Cannot drop materialized view '%s' in keyspace '%s' without base CF.", columnFamily(), keyspace())); - else - throw new InvalidRequestException(String.format("View '%s' could not be found in any of the tables of keyspace '%s'", cfName, keyspace())); - } - - CFMetaData updatedCfm = baseCfm.copy(); - updatedCfm.materializedViews(updatedCfm.getMaterializedViews().without(columnFamily())); - MigrationManager.announceColumnFamilyUpdate(updatedCfm, false, isLocalOnly); - MigrationManager.announceColumnFamilyDrop(keyspace(), columnFamily(), isLocalOnly); + MigrationManager.announceViewDrop(keyspace(), columnFamily(), isLocalOnly); return true; } catch (ConfigurationException e) diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 3855b6a506..a04af4c671 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -21,13 +21,13 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.collect.Iterables; - -import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.config.MaterializedViewDefinition; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.cql3.*; import org.apache.cassandra.cql3.ColumnIdentifier.Raw; import org.apache.cassandra.cql3.functions.Function; @@ -38,10 +38,8 @@ import org.apache.cassandra.db.filter.*; import org.apache.cassandra.db.marshal.BooleanType; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.db.rows.RowIterator; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.RequestExecutionException; -import org.apache.cassandra.exceptions.RequestValidationException; -import org.apache.cassandra.exceptions.UnauthorizedException; +import org.apache.cassandra.db.view.View; +import org.apache.cassandra.exceptions.*; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.service.StorageProxy; @@ -52,9 +50,9 @@ import org.apache.cassandra.triggers.TriggerExecutor; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; import org.apache.cassandra.utils.UUIDGen; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; +import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull; import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull; /* @@ -170,14 +168,9 @@ public abstract class ModificationStatement implements CQLStatement return cfm.isCounter(); } - public boolean isMaterializedView() + public boolean isView() { - return cfm.isMaterializedView(); - } - - public boolean hasMaterializedViews() - { - return !cfm.getMaterializedViews().isEmpty(); + return cfm.isView(); } public long getTimestamp(long now, QueryOptions options) throws InvalidRequestException @@ -203,13 +196,16 @@ public abstract class ModificationStatement implements CQLStatement if (hasConditions()) state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.SELECT); - // MV updates need to get the current state from the table, and might update the materialized views + // MV updates need to get the current state from the table, and might update the views // Require Permission.SELECT on the base table, and Permission.MODIFY on the views - if (hasMaterializedViews()) + Iterator views = View.findAll(keyspace(), columnFamily()).iterator(); + if (views.hasNext()) { state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.SELECT); - for (MaterializedViewDefinition view : cfm.getMaterializedViews()) - state.hasColumnFamilyAccess(keyspace(), view.viewName, Permission.MODIFY); + do + { + state.hasColumnFamilyAccess(keyspace(), views.next().viewName, Permission.MODIFY); + } while (views.hasNext()); } for (Function function : getFunctions()) @@ -221,7 +217,7 @@ public abstract class ModificationStatement implements CQLStatement checkFalse(hasConditions() && attrs.isTimestampSet(), "Cannot provide custom timestamp for conditional updates"); checkFalse(isCounter() && attrs.isTimestampSet(), "Cannot provide custom timestamp for counter updates"); checkFalse(isCounter() && attrs.isTimeToLiveSet(), "Cannot provide custom TTL for counter updates"); - checkFalse(isMaterializedView(), "Cannot directly modify a materialized view"); + checkFalse(isView(), "Cannot directly modify a materialized view"); } public PartitionColumns updatedColumns() diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 7ad6c09af7..18e402b2be 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -44,7 +44,7 @@ import org.apache.cassandra.db.partitions.PartitionIterator; import org.apache.cassandra.db.rows.ComplexColumnData; import org.apache.cassandra.db.rows.Row; import org.apache.cassandra.db.rows.RowIterator; -import org.apache.cassandra.db.view.MaterializedView; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.dht.AbstractBounds; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.index.Index; @@ -175,9 +175,9 @@ public class SelectStatement implements CQLStatement public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException { - if (cfm.isMaterializedView()) + if (cfm.isView()) { - CFMetaData baseTable = MaterializedView.findBaseTable(keyspace(), columnFamily()); + CFMetaData baseTable = View.findBaseTable(keyspace(), columnFamily()); if (baseTable != null) state.hasColumnFamilyAccess(keyspace(), baseTable.cfName, Permission.SELECT); } diff --git a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java index 5dd306a543..66b3da0793 100644 --- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java @@ -65,7 +65,7 @@ public class TruncateStatement extends CFStatement implements CQLStatement try { CFMetaData metaData = Schema.instance.getCFMetaData(keyspace(), columnFamily()); - if (metaData.isMaterializedView()) + if (metaData.isView()) throw new InvalidRequestException("Cannot TRUNCATE materialized view directly; must truncate base table instead"); StorageProxy.truncateBlocking(keyspace(), columnFamily()); diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index c7d8926c2e..0d6d801313 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -48,11 +48,11 @@ import org.apache.cassandra.db.commitlog.ReplayPosition; import org.apache.cassandra.db.compaction.*; import org.apache.cassandra.db.filter.ClusteringIndexFilter; import org.apache.cassandra.db.filter.DataLimits; +import org.apache.cassandra.db.view.ViewManager; import org.apache.cassandra.db.lifecycle.*; import org.apache.cassandra.db.partitions.CachedPartition; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.db.rows.CellPath; -import org.apache.cassandra.db.view.MaterializedViewManager; import org.apache.cassandra.dht.*; import org.apache.cassandra.dht.Range; import org.apache.cassandra.exceptions.ConfigurationException; @@ -193,7 +193,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean private final AtomicInteger fileIndexGenerator = new AtomicInteger(0); public final SecondaryIndexManager indexManager; - public final MaterializedViewManager materializedViewManager; + public final ViewManager.ForStore viewManager; /* These are locally held copies to be changed from the config during runtime */ private volatile DefaultInteger minCompactionThreshold; @@ -231,7 +231,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean indexManager.reload(); - materializedViewManager.reload(); // If the CF comparator has changed, we need to change the memtable, // because the old one still aliases the previous comparator. if (data.getView().getCurrentMemtable().initialComparator != metadata.comparator) @@ -377,7 +376,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean this.maxCompactionThreshold = new DefaultInteger(metadata.params.compaction.maxCompactionThreshold()); this.directories = directories; this.indexManager = new SecondaryIndexManager(this); - this.materializedViewManager = new MaterializedViewManager(this); + this.viewManager = keyspace.viewManager.forTable(metadata.cfId); this.metric = new TableMetrics(this); fileIndexGenerator.set(generation); sampleLatencyNanos = DatabaseDescriptor.getReadRpcTimeout() / 2; @@ -513,7 +512,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean data.dropSSTables(); LifecycleTransaction.waitForDeletions(); indexManager.invalidateAllIndexesBlocking(); - materializedViewManager.invalidate(); invalidateCaches(); } @@ -633,8 +631,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean // must be called after all sstables are loaded since row cache merges all row versions public void init() { - materializedViewManager.init(); - if (!isRowCacheEnabled()) return; @@ -1912,7 +1908,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean // flush the CF being truncated before forcing the new segment forceBlockingFlush(); - materializedViewManager.forceBlockingFlush(); + viewManager.forceBlockingFlush(); // sleep a little to make sure that our truncatedAt comes after any sstable // that was part of the flushed we forced; otherwise on a tie, it won't get deleted. @@ -1921,7 +1917,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean else { dumpMemtable(); - materializedViewManager.dumpMemtables(); + viewManager.dumpMemtables(); } Runnable truncateRunnable = new Runnable() @@ -1940,7 +1936,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean indexManager.truncateAllIndexesBlocking(truncatedAt); - materializedViewManager.truncateBlocking(truncatedAt); + viewManager.truncateBlocking(truncatedAt); SystemKeyspace.saveTruncationRecord(ColumnFamilyStore.this, truncatedAt, replayAfter); logger.debug("cleaning out row cache"); @@ -1974,7 +1970,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean logger.debug("Cancelling in-progress compactions for {}", metadata.cfName); Iterable selfWithAuxiliaryCfs = interruptViews - ? Iterables.concat(concatWithIndexes(), materializedViewManager.allViewsCfs()) + ? Iterables.concat(concatWithIndexes(), viewManager.allViewsCfs()) : concatWithIndexes(); for (ColumnFamilyStore cfs : selfWithAuxiliaryCfs) diff --git a/src/java/org/apache/cassandra/db/Keyspace.java b/src/java/org/apache/cassandra/db/Keyspace.java index 4661baee38..1169b45016 100644 --- a/src/java/org/apache/cassandra/db/Keyspace.java +++ b/src/java/org/apache/cassandra/db/Keyspace.java @@ -40,7 +40,7 @@ import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.lifecycle.SSTableSet; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.db.rows.UnfilteredRowIterator; -import org.apache.cassandra.db.view.MaterializedViewManager; +import org.apache.cassandra.db.view.ViewManager; import org.apache.cassandra.exceptions.WriteTimeoutException; import org.apache.cassandra.index.Index; import org.apache.cassandra.index.SecondaryIndexManager; @@ -81,12 +81,13 @@ public class Keyspace private volatile KeyspaceMetadata metadata; //OpOrder is defined globally since we need to order writes across - //Keyspaces in the case of MaterializedViews (batchlog of MV mutations) + //Keyspaces in the case of Views (batchlog of view mutations) public static final OpOrder writeOrder = new OpOrder(); /* ColumnFamilyStore per column family */ private final ConcurrentMap columnFamilyStores = new ConcurrentHashMap<>(); private volatile AbstractReplicationStrategy replicationStrategy; + public final ViewManager viewManager; public static final Function keyspaceTransformer = new Function() { @@ -305,11 +306,13 @@ public class Keyspace createReplicationStrategy(metadata); this.metric = new KeyspaceMetrics(this); - for (CFMetaData cfm : metadata.tables) + this.viewManager = new ViewManager(this); + for (CFMetaData cfm : metadata.tablesAndViews()) { logger.debug("Initializing {}.{}", getName(), cfm.cfName); initCf(cfm.cfId, cfm.cfName, loadSSTables); } + this.viewManager.reload(); } private Keyspace(KeyspaceMetadata metadata) @@ -317,6 +320,7 @@ public class Keyspace this.metadata = metadata; createReplicationStrategy(metadata); this.metric = new KeyspaceMetrics(this); + this.viewManager = new ViewManager(this); } public static Keyspace mockKS(KeyspaceMetadata metadata) @@ -418,11 +422,11 @@ public class Keyspace throw new RuntimeException("Testing write failures"); Lock lock = null; - boolean requiresViewUpdate = updateIndexes && MaterializedViewManager.updatesAffectView(Collections.singleton(mutation), false); + boolean requiresViewUpdate = updateIndexes && viewManager.updatesAffectView(Collections.singleton(mutation), false); if (requiresViewUpdate) { - lock = MaterializedViewManager.acquireLockFor(mutation.key().getKey()); + lock = ViewManager.acquireLockFor(mutation.key().getKey()); if (lock == null) { @@ -430,11 +434,11 @@ public class Keyspace { logger.debug("Could not acquire lock for {}", ByteBufferUtil.bytesToHex(mutation.key().getKey())); Tracing.trace("Could not acquire MV lock"); - throw new WriteTimeoutException(WriteType.MATERIALIZED_VIEW, ConsistencyLevel.LOCAL_ONE, 0, 1); + throw new WriteTimeoutException(WriteType.VIEW, ConsistencyLevel.LOCAL_ONE, 0, 1); } else { - //This MV update can't happen right now. so rather than keep this thread busy + //This view update can't happen right now. so rather than keep this thread busy // we will re-apply ourself to the queue and try again later StageManager.getStage(Stage.MUTATION).execute(() -> { if (writeCommitLog) @@ -472,7 +476,7 @@ public class Keyspace try { Tracing.trace("Creating materialized view mutations from base table replica"); - cfs.materializedViewManager.pushViewReplicaUpdates(upd, !isClReplay); + viewManager.pushViewReplicaUpdates(upd, !isClReplay); } catch (Throwable t) { diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java index d54ee8bd83..6b2585e14a 100644 --- a/src/java/org/apache/cassandra/db/SystemKeyspace.java +++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java @@ -95,8 +95,8 @@ public final class SystemKeyspace public static final String SSTABLE_ACTIVITY = "sstable_activity"; public static final String SIZE_ESTIMATES = "size_estimates"; public static final String AVAILABLE_RANGES = "available_ranges"; - public static final String MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS = "materialized_views_builds_in_progress"; - public static final String BUILT_MATERIALIZED_VIEWS = "built_materialized_views"; + public static final String VIEWS_BUILDS_IN_PROGRESS = "views_builds_in_progress"; + public static final String BUILT_VIEWS = "built_views"; @Deprecated public static final String LEGACY_HINTS = "hints"; @Deprecated public static final String LEGACY_BATCHLOG = "batchlog"; @@ -246,9 +246,9 @@ public final class SystemKeyspace + "ranges set," + "PRIMARY KEY ((keyspace_name)))"); - private static final CFMetaData MaterializedViewsBuildsInProgress = - compile(MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS, - "materialized views builds current progress", + private static final CFMetaData ViewsBuildsInProgress = + compile(VIEWS_BUILDS_IN_PROGRESS, + "views builds current progress", "CREATE TABLE %s (" + "keyspace_name text," + "view_name text," @@ -256,9 +256,9 @@ public final class SystemKeyspace + "generation_number int," + "PRIMARY KEY ((keyspace_name), view_name))"); - private static final CFMetaData BuiltMaterializedViews = - compile(BUILT_MATERIALIZED_VIEWS, - "built materialized views", + private static final CFMetaData BuiltViews = + compile(BUILT_VIEWS, + "built views", "CREATE TABLE %s (" + "keyspace_name text," + "view_name text," @@ -414,7 +414,7 @@ public final class SystemKeyspace public static KeyspaceMetadata metadata() { - return KeyspaceMetadata.create(NAME, KeyspaceParams.local(), tables(), Types.none(), functions()); + return KeyspaceMetadata.create(NAME, KeyspaceParams.local(), tables(), Views.none(), Types.none(), functions()); } private static Tables tables() @@ -430,8 +430,8 @@ public final class SystemKeyspace SSTableActivity, SizeEstimates, AvailableRanges, - MaterializedViewsBuildsInProgress, - BuiltMaterializedViews, + ViewsBuildsInProgress, + BuiltViews, LegacyHints, LegacyBatchlog, LegacyKeyspaces, @@ -531,61 +531,61 @@ public final class SystemKeyspace public static boolean isViewBuilt(String keyspaceName, String viewName) { String req = "SELECT view_name FROM %s.\"%s\" WHERE keyspace_name=? AND view_name=?"; - UntypedResultSet result = executeInternal(String.format(req, NAME, BUILT_MATERIALIZED_VIEWS), keyspaceName, viewName); + UntypedResultSet result = executeInternal(String.format(req, NAME, BUILT_VIEWS), keyspaceName, viewName); return !result.isEmpty(); } - public static void setMaterializedViewBuilt(String keyspaceName, String viewName) + public static void setViewBuilt(String keyspaceName, String viewName) { String req = "INSERT INTO %s.\"%s\" (keyspace_name, view_name) VALUES (?, ?)"; - executeInternal(String.format(req, NAME, BUILT_MATERIALIZED_VIEWS), keyspaceName, viewName); - forceBlockingFlush(BUILT_MATERIALIZED_VIEWS); + executeInternal(String.format(req, NAME, BUILT_VIEWS), keyspaceName, viewName); + forceBlockingFlush(BUILT_VIEWS); } - public static void setMaterializedViewRemoved(String keyspaceName, String viewName) + public static void setViewRemoved(String keyspaceName, String viewName) { String buildReq = "DELETE FROM %S.%s WHERE keyspace_name = ? AND view_name = ?"; - executeInternal(String.format(buildReq, NAME, MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS), keyspaceName, viewName); - forceBlockingFlush(MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS); + executeInternal(String.format(buildReq, NAME, VIEWS_BUILDS_IN_PROGRESS), keyspaceName, viewName); + forceBlockingFlush(VIEWS_BUILDS_IN_PROGRESS); String builtReq = "DELETE FROM %s.\"%s\" WHERE keyspace_name = ? AND view_name = ?"; - executeInternal(String.format(builtReq, NAME, BUILT_MATERIALIZED_VIEWS), keyspaceName, viewName); - forceBlockingFlush(BUILT_MATERIALIZED_VIEWS); + executeInternal(String.format(builtReq, NAME, BUILT_VIEWS), keyspaceName, viewName); + forceBlockingFlush(BUILT_VIEWS); } - public static void beginMaterializedViewBuild(String ksname, String viewName, int generationNumber) + public static void beginViewBuild(String ksname, String viewName, int generationNumber) { - executeInternal(String.format("INSERT INTO system.%s (keyspace_name, view_name, generation_number) VALUES (?, ?, ?)", MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS), + executeInternal(String.format("INSERT INTO system.%s (keyspace_name, view_name, generation_number) VALUES (?, ?, ?)", VIEWS_BUILDS_IN_PROGRESS), ksname, viewName, generationNumber); } - public static void finishMaterializedViewBuildStatus(String ksname, String viewName) + public static void finishViewBuildStatus(String ksname, String viewName) { // We flush the view built first, because if we fail now, we'll restart at the last place we checkpointed - // materialized view build. + // view build. // If we flush the delete first, we'll have to restart from the beginning. - // Also, if the build succeeded, but the materialized view build failed, we will be able to skip the - // materialized view build check next boot. - setMaterializedViewBuilt(ksname, viewName); - forceBlockingFlush(BUILT_MATERIALIZED_VIEWS); - executeInternal(String.format("DELETE FROM system.%s WHERE keyspace_name = ? AND view_name = ?", MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS), ksname, viewName); - forceBlockingFlush(MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS); + // Also, if the build succeeded, but the view build failed, we will be able to skip the view build check + // next boot. + setViewBuilt(ksname, viewName); + forceBlockingFlush(BUILT_VIEWS); + executeInternal(String.format("DELETE FROM system.%s WHERE keyspace_name = ? AND view_name = ?", VIEWS_BUILDS_IN_PROGRESS), ksname, viewName); + forceBlockingFlush(VIEWS_BUILDS_IN_PROGRESS); } - public static void updateMaterializedViewBuildStatus(String ksname, String viewName, Token token) + public static void updateViewBuildStatus(String ksname, String viewName, Token token) { String req = "INSERT INTO system.%s (keyspace_name, view_name, last_token) VALUES (?, ?, ?)"; - Token.TokenFactory factory = MaterializedViewsBuildsInProgress.partitioner.getTokenFactory(); - executeInternal(String.format(req, MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS), ksname, viewName, factory.toString(token)); + Token.TokenFactory factory = ViewsBuildsInProgress.partitioner.getTokenFactory(); + executeInternal(String.format(req, VIEWS_BUILDS_IN_PROGRESS), ksname, viewName, factory.toString(token)); } - public static Pair getMaterializedViewBuildStatus(String ksname, String viewName) + public static Pair getViewBuildStatus(String ksname, String viewName) { String req = "SELECT generation_number, last_token FROM system.%s WHERE keyspace_name = ? AND view_name = ?"; - UntypedResultSet queryResultSet = executeInternal(String.format(req, MATERIALIZED_VIEWS_BUILDS_IN_PROGRESS), ksname, viewName); + UntypedResultSet queryResultSet = executeInternal(String.format(req, VIEWS_BUILDS_IN_PROGRESS), ksname, viewName); if (queryResultSet == null || queryResultSet.isEmpty()) return null; @@ -597,7 +597,7 @@ public final class SystemKeyspace generation = row.getInt("generation_number"); if (row.has("last_key")) { - Token.TokenFactory factory = MaterializedViewsBuildsInProgress.partitioner.getTokenFactory(); + Token.TokenFactory factory = ViewsBuildsInProgress.partitioner.getTokenFactory(); lastKey = factory.fromString(row.getString("last_key")); } diff --git a/src/java/org/apache/cassandra/db/WriteType.java b/src/java/org/apache/cassandra/db/WriteType.java index 20fb6a9f23..fdbe97d8df 100644 --- a/src/java/org/apache/cassandra/db/WriteType.java +++ b/src/java/org/apache/cassandra/db/WriteType.java @@ -25,5 +25,5 @@ public enum WriteType COUNTER, BATCH_LOG, CAS, - MATERIALIZED_VIEW; + VIEW; } diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java index 5207f49811..75d50e76f3 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java @@ -45,7 +45,7 @@ import org.apache.cassandra.db.lifecycle.LifecycleTransaction; import org.apache.cassandra.db.lifecycle.SSTableSet; import org.apache.cassandra.db.lifecycle.View; import org.apache.cassandra.db.rows.UnfilteredRowIterator; -import org.apache.cassandra.db.view.MaterializedViewBuilder; +import org.apache.cassandra.db.view.ViewBuilder; import org.apache.cassandra.dht.Bounds; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; @@ -1334,7 +1334,7 @@ public class CompactionManager implements CompactionManagerMBean } } - public Future submitMaterializedViewBuilder(final MaterializedViewBuilder builder) + public Future submitViewBuilder(final ViewBuilder builder) { Runnable runnable = new Runnable() { diff --git a/src/java/org/apache/cassandra/db/compaction/OperationType.java b/src/java/org/apache/cassandra/db/compaction/OperationType.java index f8f016cb5a..a69622b714 100644 --- a/src/java/org/apache/cassandra/db/compaction/OperationType.java +++ b/src/java/org/apache/cassandra/db/compaction/OperationType.java @@ -36,7 +36,7 @@ public enum OperationType FLUSH("Flush"), STREAM("Stream"), WRITE("Write"), - VIEW_BUILD("Materialized view build"); + VIEW_BUILD("View build"); public final String type; public final String fileName; diff --git a/src/java/org/apache/cassandra/db/view/MaterializedViewManager.java b/src/java/org/apache/cassandra/db/view/MaterializedViewManager.java deleted file mode 100644 index 41f4ed0850..0000000000 --- a/src/java/org/apache/cassandra/db/view/MaterializedViewManager.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.cassandra.db.view; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.locks.Lock; - -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Striped; - -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.config.MaterializedViewDefinition; -import org.apache.cassandra.db.ColumnFamilyStore; -import org.apache.cassandra.db.IMutation; -import org.apache.cassandra.db.Keyspace; -import org.apache.cassandra.db.Mutation; -import org.apache.cassandra.db.SystemKeyspace; -import org.apache.cassandra.db.commitlog.ReplayPosition; -import org.apache.cassandra.db.partitions.PartitionUpdate; -import org.apache.cassandra.dht.Token; -import org.apache.cassandra.exceptions.OverloadedException; -import org.apache.cassandra.exceptions.UnavailableException; -import org.apache.cassandra.exceptions.WriteTimeoutException; -import org.apache.cassandra.service.StorageProxy; -import org.apache.cassandra.service.StorageService; - -/** - * Manages {@link MaterializedView}'s for a single {@link ColumnFamilyStore}. All of the materialized views for that - * table are created when this manager is initialized. - * - * The main purposes of the manager are to provide a single location for updates to be vetted to see whether they update - * any views {@link MaterializedViewManager#updateAffectsView(PartitionUpdate)}, provide locks to prevent multiple - * updates from creating incoherent updates in the view {@link MaterializedViewManager#acquireLockFor(ByteBuffer)}, and - * to affect change on the view. - */ -public class MaterializedViewManager -{ - private static final Striped LOCKS = Striped.lazyWeakLock(DatabaseDescriptor.getConcurrentWriters() * 1024); - private static final boolean enableCoordinatorBatchlog = Boolean.getBoolean("cassandra.mv_enable_coordinator_batchlog"); - - private final ConcurrentNavigableMap viewsByName; - - private final ColumnFamilyStore baseCfs; - - public MaterializedViewManager(ColumnFamilyStore baseCfs) - { - this.viewsByName = new ConcurrentSkipListMap<>(); - - this.baseCfs = baseCfs; - } - - public Iterable allViews() - { - return viewsByName.values(); - } - - public Iterable allViewsCfs() - { - List viewColumnFamilies = new ArrayList<>(); - for (MaterializedView view : allViews()) - viewColumnFamilies.add(view.getViewCfs()); - return viewColumnFamilies; - } - - public void init() - { - reload(); - } - - public void invalidate() - { - for (MaterializedView view : allViews()) - removeMaterializedView(view.name); - } - - public void reload() - { - Map newViewsByName = new HashMap<>(); - for (MaterializedViewDefinition definition : baseCfs.metadata.getMaterializedViews()) - { - newViewsByName.put(definition.viewName, definition); - } - - for (String viewName : viewsByName.keySet()) - { - if (!newViewsByName.containsKey(viewName)) - removeMaterializedView(viewName); - } - - for (Map.Entry entry : newViewsByName.entrySet()) - { - if (!viewsByName.containsKey(entry.getKey())) - addMaterializedView(entry.getValue()); - } - - for (MaterializedView view : allViews()) - { - view.build(); - // We provide the new definition from the base metadata - view.updateDefinition(newViewsByName.get(view.name)); - } - } - - public void buildAllViews() - { - for (MaterializedView view : allViews()) - view.build(); - } - - public void removeMaterializedView(String name) - { - MaterializedView view = viewsByName.remove(name); - - if (view == null) - return; - - SystemKeyspace.setMaterializedViewRemoved(baseCfs.metadata.ksName, view.name); - } - - public void addMaterializedView(MaterializedViewDefinition definition) - { - MaterializedView view = new MaterializedView(definition, baseCfs); - - viewsByName.put(definition.viewName, view); - } - - /** - * Calculates and pushes updates to the views replicas. The replicas are determined by - * {@link MaterializedViewUtils#getViewNaturalEndpoint(String, Token, Token)}. - */ - public void pushViewReplicaUpdates(PartitionUpdate update, boolean writeCommitLog) - { - List mutations = null; - TemporalRow.Set temporalRows = null; - for (Map.Entry view : viewsByName.entrySet()) - { - - temporalRows = view.getValue().getTemporalRowSet(update, temporalRows, false); - - Collection viewMutations = view.getValue().createMutations(update, temporalRows, false); - if (viewMutations != null && !viewMutations.isEmpty()) - { - if (mutations == null) - mutations = Lists.newLinkedList(); - mutations.addAll(viewMutations); - } - } - if (mutations != null) - { - StorageProxy.mutateMV(update.partitionKey().getKey(), mutations, writeCommitLog); - } - } - - public boolean updateAffectsView(PartitionUpdate upd) - { - for (MaterializedView view : allViews()) - { - if (view.updateAffectsView(upd)) - return true; - } - return false; - } - - public static Lock acquireLockFor(ByteBuffer key) - { - Lock lock = LOCKS.get(key); - - if (lock.tryLock()) - return lock; - - return null; - } - - public static boolean updatesAffectView(Collection mutations, boolean coordinatorBatchlog) - { - if (coordinatorBatchlog && !enableCoordinatorBatchlog) - return false; - - for (IMutation mutation : mutations) - { - for (PartitionUpdate cf : mutation.getPartitionUpdates()) - { - Keyspace keyspace = Keyspace.open(cf.metadata().ksName); - - if (coordinatorBatchlog && keyspace.getReplicationStrategy().getReplicationFactor() == 1) - continue; - - MaterializedViewManager viewManager = keyspace.getColumnFamilyStore(cf.metadata().cfId).materializedViewManager; - if (viewManager.updateAffectsView(cf)) - return true; - } - } - - return false; - } - - - public void forceBlockingFlush() - { - for (ColumnFamilyStore viewCfs : allViewsCfs()) - viewCfs.forceBlockingFlush(); - } - - public void dumpMemtables() - { - for (ColumnFamilyStore viewCfs : allViewsCfs()) - viewCfs.dumpMemtable(); - } - - public void truncateBlocking(long truncatedAt) - { - for (ColumnFamilyStore viewCfs : allViewsCfs()) - { - ReplayPosition replayAfter = viewCfs.discardSSTables(truncatedAt); - SystemKeyspace.saveTruncationRecord(viewCfs, truncatedAt, replayAfter); - } - } -} diff --git a/src/java/org/apache/cassandra/db/view/MaterializedView.java b/src/java/org/apache/cassandra/db/view/View.java similarity index 81% rename from src/java/org/apache/cassandra/db/view/MaterializedView.java rename to src/java/org/apache/cassandra/db/view/View.java index 52034cc4ca..7bcb59217f 100644 --- a/src/java/org/apache/cassandra/db/view/MaterializedView.java +++ b/src/java/org/apache/cassandra/db/view/View.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.UUID; import javax.annotation.Nullable; @@ -32,10 +33,9 @@ import com.google.common.collect.Iterables; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.config.MaterializedViewDefinition; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.config.Schema; import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.cql3.statements.CFProperties; import org.apache.cassandra.db.AbstractReadCommandBuilder.SinglePartitionSliceBuilder; import org.apache.cassandra.db.CBuilder; import org.apache.cassandra.db.Clustering; @@ -43,7 +43,6 @@ import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.DeletionInfo; import org.apache.cassandra.db.DeletionTime; -import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.LivenessInfo; import org.apache.cassandra.db.Mutation; import org.apache.cassandra.db.RangeTombstone; @@ -65,23 +64,23 @@ import org.apache.cassandra.schema.KeyspaceMetadata; import org.apache.cassandra.service.pager.QueryPager; /** - * A Materialized View copies data from a base table into a view table which can be queried independently from the - * base. Every update which targets the base table must be fed through the {@link MaterializedViewManager} to ensure + * A View copies data from a base table into a view table which can be queried independently from the + * base. Every update which targets the base table must be fed through the {@link ViewManager} to ensure * that if a view needs to be updated, the updates are properly created and fed into the view. * * This class does the job of translating the base row to the view row. * * It handles reading existing state and figuring out what tombstones need to be generated. * - * createMutations below is the "main method" + * {@link View#createMutations(AbstractBTreePartition, TemporalRow.Set, boolean)} is the "main method" * */ -public class MaterializedView +public class View { /** * The columns should all be updated together, so we use this object as group. */ - private static class MVColumns + private static class Columns { //These are the base column definitions in terms of the *views* partitioning. //Meaning we can see (for example) the partition key of the view contains a clustering key @@ -90,7 +89,7 @@ public class MaterializedView public final List primaryKeyDefs; public final List baseComplexColumns; - private MVColumns(List partitionDefs, List primaryKeyDefs, List baseComplexColumns) + private Columns(List partitionDefs, List primaryKeyDefs, List baseComplexColumns) { this.partitionDefs = partitionDefs; this.primaryKeyDefs = primaryKeyDefs; @@ -99,42 +98,32 @@ public class MaterializedView } public final String name; + private volatile ViewDefinition definition; private final ColumnFamilyStore baseCfs; - private ColumnFamilyStore _viewCfs = null; - private MVColumns columns; + private Columns columns; private final boolean viewHasAllPrimaryKeys; - private final boolean includeAll; - private MaterializedViewBuilder builder; + private final boolean includeAllColumns; + private ViewBuilder builder; - public MaterializedView(MaterializedViewDefinition definition, - ColumnFamilyStore baseCfs) + public View(ViewDefinition definition, + ColumnFamilyStore baseCfs) { this.baseCfs = baseCfs; name = definition.viewName; - includeAll = definition.includeAll; + includeAllColumns = definition.includeAllColumns; viewHasAllPrimaryKeys = updateDefinition(definition); } - /** - * Lazily fetch the CFS instance for the view. - * We do this lazily to avoid initilization issues. - * - * @return The views CFS instance - */ - public ColumnFamilyStore getViewCfs() + public ViewDefinition getDefinition() { - if (_viewCfs == null) - _viewCfs = Keyspace.openAndGetStore(Schema.instance.getCFMetaData(baseCfs.keyspace.getName(), name)); - - return _viewCfs; + return definition; } - /** * Lookup column definitions in the base table that correspond to the view columns (should be 1:1) * @@ -170,17 +159,20 @@ public class MaterializedView * @return true if the view contains only columns which are part of the base's primary key; false if there is at * least one column which is not. */ - public boolean updateDefinition(MaterializedViewDefinition definition) + public boolean updateDefinition(ViewDefinition definition) { - List partitionDefs = new ArrayList<>(definition.partitionColumns.size()); - List primaryKeyDefs = new ArrayList<>(definition.partitionColumns.size() - + definition.clusteringColumns.size()); + this.definition = definition; + + CFMetaData viewCfm = definition.metadata; + List partitionDefs = new ArrayList<>(viewCfm.partitionKeyColumns().size()); + List primaryKeyDefs = new ArrayList<>(viewCfm.partitionKeyColumns().size() + + viewCfm.clusteringColumns().size()); List baseComplexColumns = new ArrayList<>(); // We only add the partition columns to the partitions list, but both partition columns and clustering // columns are added to the primary keys list - boolean partitionAllPrimaryKeyColumns = resolveAndAddColumns(definition.partitionColumns, primaryKeyDefs, partitionDefs); - boolean clusteringAllPrimaryKeyColumns = resolveAndAddColumns(definition.clusteringColumns, primaryKeyDefs); + boolean partitionAllPrimaryKeyColumns = resolveAndAddColumns(Iterables.transform(viewCfm.partitionKeyColumns(), cd -> cd.name), primaryKeyDefs, partitionDefs); + boolean clusteringAllPrimaryKeyColumns = resolveAndAddColumns(Iterables.transform(viewCfm.clusteringColumns(), cd -> cd.name), primaryKeyDefs); for (ColumnDefinition cdef : baseCfs.metadata.allColumns()) { @@ -190,7 +182,7 @@ public class MaterializedView } } - this.columns = new MVColumns(partitionDefs, primaryKeyDefs, baseComplexColumns); + this.columns = new Columns(partitionDefs, primaryKeyDefs, baseComplexColumns); return partitionAllPrimaryKeyColumns && clusteringAllPrimaryKeyColumns; } @@ -211,10 +203,10 @@ public class MaterializedView public boolean updateAffectsView(AbstractBTreePartition partition) { // If we are including all of the columns, then any update will be included - if (includeAll) + if (includeAllColumns) return true; - // If there are range tombstones, tombstones will also need to be generated for the materialized view + // If there are range tombstones, tombstones will also need to be generated for the view // This requires a query of the base rows and generating tombstones for all of those values if (!partition.deletionInfo().isLive()) return true; @@ -229,7 +221,7 @@ public class MaterializedView for (ColumnData data : row) { - if (getViewCfs().metadata.getColumnDefinition(data.column().name) != null) + if (definition.metadata.getColumnDefinition(data.column().name) != null) return true; } } @@ -246,9 +238,9 @@ public class MaterializedView */ private Clustering viewClustering(TemporalRow temporalRow, TemporalRow.Resolver resolver) { - CFMetaData viewCfm = getViewCfs().metadata; + CFMetaData viewCfm = definition.metadata; int numViewClustering = viewCfm.clusteringColumns().size(); - CBuilder clustering = CBuilder.create(getViewCfs().getComparator()); + CBuilder clustering = CBuilder.create(viewCfm.comparator); for (int i = 0; i < numViewClustering; i++) { ColumnDefinition definition = viewCfm.clusteringColumns().get(i); @@ -267,7 +259,7 @@ public class MaterializedView TemporalRow.Resolver resolver, int nowInSec) { - CFMetaData viewCfm = getViewCfs().metadata; + CFMetaData viewCfm = definition.metadata; Row.Builder builder = BTreeRow.unsortedBuilder(nowInSec); builder.newRow(viewClustering(temporalRow, resolver)); builder.addRowDeletion(deletion); @@ -284,8 +276,7 @@ public class MaterializedView TemporalRow.Resolver resolver, int nowInSec) { - - CFMetaData viewCfm = getViewCfs().metadata; + CFMetaData viewCfm = definition.metadata; Row.Builder builder = BTreeRow.unsortedBuilder(nowInSec); builder.newRow(viewClustering(temporalRow, resolver)); builder.addComplexDeletion(deletedColumn, deletionTime); @@ -311,7 +302,7 @@ public class MaterializedView partitionKey[i] = value; } - CFMetaData metadata = getViewCfs().metadata; + CFMetaData metadata = definition.metadata; return metadata.decorateKey(CFMetaData.serializePartitionKey(metadata .getKeyValidatorAsClusteringComparator() .make(partitionKey))); @@ -348,14 +339,14 @@ public class MaterializedView } /** - * @return Mutation which is the transformed base table mutation for the materialized view. + * @return Mutation which is the transformed base table mutation for the view. */ private PartitionUpdate createUpdatesForInserts(TemporalRow temporalRow) { TemporalRow.Resolver resolver = TemporalRow.latest; DecoratedKey partitionKey = viewPartitionKey(temporalRow, resolver); - ColumnFamilyStore viewCfs = getViewCfs(); + CFMetaData viewCfm = definition.metadata; if (partitionKey == null) { @@ -365,18 +356,18 @@ public class MaterializedView Row.Builder regularBuilder = BTreeRow.unsortedBuilder(temporalRow.nowInSec); - CBuilder clustering = CBuilder.create(viewCfs.getComparator()); - for (int i = 0; i < viewCfs.metadata.clusteringColumns().size(); i++) + CBuilder clustering = CBuilder.create(viewCfm.comparator); + for (int i = 0; i < viewCfm.clusteringColumns().size(); i++) { - clustering.add(temporalRow.clusteringValue(viewCfs.metadata.clusteringColumns().get(i), resolver)); + clustering.add(temporalRow.clusteringValue(viewCfm.clusteringColumns().get(i), resolver)); } regularBuilder.newRow(clustering.build()); - regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(viewCfs.metadata, + regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(viewCfm, temporalRow.viewClusteringTimestamp(), temporalRow.viewClusteringTtl(), temporalRow.viewClusteringLocalDeletionTime())); - for (ColumnDefinition columnDefinition : viewCfs.metadata.allColumns()) + for (ColumnDefinition columnDefinition : viewCfm.allColumns()) { if (columnDefinition.isPrimaryKeyColumn()) continue; @@ -387,7 +378,7 @@ public class MaterializedView } } - return PartitionUpdate.singleRowUpdate(viewCfs.metadata, partitionKey, regularBuilder.build()); + return PartitionUpdate.singleRowUpdate(viewCfm, partitionKey, regularBuilder.build()); } /** @@ -597,7 +588,7 @@ public class MaterializedView for (ColumnDefinition def : this.columns.primaryKeyDefs) columns.add(def.name); - TemporalRow.Set rowSet = null; + TemporalRow.Set rowSet; if (existing == null) { rowSet = separateRows(partition, columns); @@ -671,79 +662,21 @@ public class MaterializedView this.builder = null; } - this.builder = new MaterializedViewBuilder(baseCfs, this); - CompactionManager.instance.submitMaterializedViewBuilder(builder); + this.builder = new ViewBuilder(baseCfs, this); + CompactionManager.instance.submitViewBuilder(builder); } @Nullable - public static CFMetaData findBaseTable(String keyspace, String view) + public static CFMetaData findBaseTable(String keyspace, String viewName) + { + ViewDefinition view = Schema.instance.getView(keyspace, viewName); + return (view == null) ? null : Schema.instance.getCFMetaData(view.baseTableId); + } + + public static Iterable findAll(String keyspace, String baseTable) { KeyspaceMetadata ksm = Schema.instance.getKSMetaData(keyspace); - if (ksm == null) - return null; - - for (CFMetaData cfm : ksm.tables) - if (cfm.getMaterializedViews().get(view).isPresent()) - return cfm; - - return null; - } - - /** - * @return CFMetaData which represents the definition given - */ - public static CFMetaData getCFMetaData(MaterializedViewDefinition definition, - CFMetaData baseCf, - CFProperties properties) - { - CFMetaData.Builder viewBuilder = CFMetaData.Builder - .createView(baseCf.ksName, definition.viewName); - - ColumnDefinition nonPkTarget = null; - - for (ColumnIdentifier targetIdentifier : definition.partitionColumns) - { - ColumnDefinition target = baseCf.getColumnDefinition(targetIdentifier); - if (!target.isPartitionKey()) - nonPkTarget = target; - - viewBuilder.addPartitionKey(target.name, properties.getReversableType(targetIdentifier, target.type)); - } - - Collection included = new ArrayList<>(); - for(ColumnIdentifier identifier : definition.included) - { - ColumnDefinition cfDef = baseCf.getColumnDefinition(identifier); - assert cfDef != null; - included.add(cfDef); - } - - boolean includeAll = included.isEmpty(); - - for (ColumnIdentifier ident : definition.clusteringColumns) - { - ColumnDefinition column = baseCf.getColumnDefinition(ident); - viewBuilder.addClusteringColumn(ident, properties.getReversableType(ident, column.type)); - } - - for (ColumnDefinition column : baseCf.partitionColumns().regulars) - { - if (column != nonPkTarget && (includeAll || included.contains(column))) - { - viewBuilder.addRegularColumn(column.name, column.type); - } - } - - //Add any extra clustering columns - for (ColumnDefinition column : Iterables.concat(baseCf.partitionKeyColumns(), baseCf.clusteringColumns())) - { - if ( (!definition.partitionColumns.contains(column.name) && !definition.clusteringColumns.contains(column.name)) && - (includeAll || included.contains(column)) ) - { - viewBuilder.addRegularColumn(column.name, column.type); - } - } - - return viewBuilder.build().params(properties.properties.asNewTableParams()); + final UUID baseId = Schema.instance.getId(keyspace, baseTable); + return Iterables.filter(ksm.views, view -> view.baseTableId.equals(baseId)); } } diff --git a/src/java/org/apache/cassandra/db/view/MaterializedViewBuilder.java b/src/java/org/apache/cassandra/db/view/ViewBuilder.java similarity index 87% rename from src/java/org/apache/cassandra/db/view/MaterializedViewBuilder.java rename to src/java/org/apache/cassandra/db/view/ViewBuilder.java index e23fd84299..62aa3324e1 100644 --- a/src/java/org/apache/cassandra/db/view/MaterializedViewBuilder.java +++ b/src/java/org/apache/cassandra/db/view/ViewBuilder.java @@ -39,7 +39,6 @@ import org.apache.cassandra.db.compaction.CompactionInfo; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.db.lifecycle.SSTableSet; -import org.apache.cassandra.db.lifecycle.View; import org.apache.cassandra.db.partitions.FilteredPartition; import org.apache.cassandra.db.partitions.PartitionIterator; import org.apache.cassandra.db.rows.RowIterator; @@ -57,18 +56,18 @@ import org.apache.cassandra.utils.Pair; import org.apache.cassandra.utils.UUIDGen; import org.apache.cassandra.utils.concurrent.Refs; -public class MaterializedViewBuilder extends CompactionInfo.Holder +public class ViewBuilder extends CompactionInfo.Holder { private final ColumnFamilyStore baseCfs; - private final MaterializedView view; + private final View view; private final UUID compactionId; private volatile Token prevToken = null; - private static final Logger logger = LoggerFactory.getLogger(MaterializedViewBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(ViewBuilder.class); private volatile boolean isStopped = false; - public MaterializedViewBuilder(ColumnFamilyStore baseCfs, MaterializedView view) + public ViewBuilder(ColumnFamilyStore baseCfs, View view) { this.baseCfs = baseCfs; this.view = view; @@ -109,13 +108,13 @@ public class MaterializedViewBuilder extends CompactionInfo.Holder return; Iterable> ranges = StorageService.instance.getLocalRanges(baseCfs.metadata.ksName); - final Pair buildStatus = SystemKeyspace.getMaterializedViewBuildStatus(ksname, viewName); + final Pair buildStatus = SystemKeyspace.getViewBuildStatus(ksname, viewName); Token lastToken; - Function> function; + Function> function; if (buildStatus == null) { baseCfs.forceBlockingFlush(); - function = View.select(SSTableSet.CANONICAL); + function = org.apache.cassandra.db.lifecycle.View.select(SSTableSet.CANONICAL); int generation = Integer.MIN_VALUE; try (Refs temp = baseCfs.selectAndReference(function).refs) @@ -126,17 +125,17 @@ public class MaterializedViewBuilder extends CompactionInfo.Holder } } - SystemKeyspace.beginMaterializedViewBuild(ksname, viewName, generation); + SystemKeyspace.beginViewBuild(ksname, viewName, generation); lastToken = null; } else { - function = new Function>() + function = new Function>() { @Nullable - public Iterable apply(View view) + public Iterable apply(org.apache.cassandra.db.lifecycle.View view) { - Iterable readers = View.select(SSTableSet.CANONICAL).apply(view); + Iterable readers = org.apache.cassandra.db.lifecycle.View.select(SSTableSet.CANONICAL).apply(view); if (readers != null) return Iterables.filter(readers, ssTableReader -> ssTableReader.descriptor.generation <= buildStatus.left); return null; @@ -163,7 +162,7 @@ public class MaterializedViewBuilder extends CompactionInfo.Holder if (prevToken == null || prevToken.compareTo(token) != 0) { - SystemKeyspace.updateMaterializedViewBuildStatus(ksname, viewName, key.getToken()); + SystemKeyspace.updateViewBuildStatus(ksname, viewName, key.getToken()); prevToken = token; } } @@ -173,13 +172,13 @@ public class MaterializedViewBuilder extends CompactionInfo.Holder } if (!isStopped) - SystemKeyspace.finishMaterializedViewBuildStatus(ksname, viewName); + SystemKeyspace.finishViewBuildStatus(ksname, viewName); } catch (Exception e) { - final MaterializedViewBuilder builder = new MaterializedViewBuilder(baseCfs, view); - ScheduledExecutors.nonPeriodicTasks.schedule(() -> CompactionManager.instance.submitMaterializedViewBuilder(builder), + final ViewBuilder builder = new ViewBuilder(baseCfs, view); + ScheduledExecutors.nonPeriodicTasks.schedule(() -> CompactionManager.instance.submitViewBuilder(builder), 5, TimeUnit.MINUTES); logger.warn("Materialized View failed to complete, sleeping 5 minutes before restarting", e); diff --git a/src/java/org/apache/cassandra/db/view/ViewManager.java b/src/java/org/apache/cassandra/db/view/ViewManager.java new file mode 100644 index 0000000000..2364ed1284 --- /dev/null +++ b/src/java/org/apache/cassandra/db/view/ViewManager.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db.view; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.locks.Lock; + +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.Striped; + +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.config.ViewDefinition; +import org.apache.cassandra.db.ColumnFamilyStore; +import org.apache.cassandra.db.IMutation; +import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.db.Mutation; +import org.apache.cassandra.db.SystemKeyspace; +import org.apache.cassandra.db.commitlog.ReplayPosition; +import org.apache.cassandra.db.partitions.PartitionUpdate; +import org.apache.cassandra.dht.Token; +import org.apache.cassandra.service.StorageProxy; + +/** + * Manages {@link View}'s for a single {@link ColumnFamilyStore}. All of the views for that table are created when this + * manager is initialized. + * + * The main purposes of the manager are to provide a single location for updates to be vetted to see whether they update + * any views {@link ViewManager#updatesAffectView(Collection, boolean)}, provide locks to prevent multiple + * updates from creating incoherent updates in the view {@link ViewManager#acquireLockFor(ByteBuffer)}, and + * to affect change on the view. + */ +public class ViewManager +{ + public class ForStore + { + private final ConcurrentNavigableMap viewsByName; + + public ForStore() + { + this.viewsByName = new ConcurrentSkipListMap<>(); + } + + public Iterable allViews() + { + return viewsByName.values(); + } + + public Iterable allViewsCfs() + { + List viewColumnFamilies = new ArrayList<>(); + for (View view : allViews()) + viewColumnFamilies.add(keyspace.getColumnFamilyStore(view.getDefinition().viewName)); + return viewColumnFamilies; + } + + public void forceBlockingFlush() + { + for (ColumnFamilyStore viewCfs : allViewsCfs()) + viewCfs.forceBlockingFlush(); + } + + public void dumpMemtables() + { + for (ColumnFamilyStore viewCfs : allViewsCfs()) + viewCfs.dumpMemtable(); + } + + public void truncateBlocking(long truncatedAt) + { + for (ColumnFamilyStore viewCfs : allViewsCfs()) + { + ReplayPosition replayAfter = viewCfs.discardSSTables(truncatedAt); + SystemKeyspace.saveTruncationRecord(viewCfs, truncatedAt, replayAfter); + } + } + + public void addView(View view) + { + viewsByName.put(view.name, view); + } + + public void removeView(String name) + { + viewsByName.remove(name); + } + } + + private static final Striped LOCKS = Striped.lazyWeakLock(DatabaseDescriptor.getConcurrentViewWriters() * 1024); + + private static final boolean enableCoordinatorBatchlog = Boolean.getBoolean("cassandra.mv_enable_coordinator_batchlog"); + + private final ConcurrentNavigableMap viewManagersByStore; + private final ConcurrentNavigableMap viewsByName; + private final Keyspace keyspace; + + public ViewManager(Keyspace keyspace) + { + this.viewManagersByStore = new ConcurrentSkipListMap<>(); + this.viewsByName = new ConcurrentSkipListMap<>(); + this.keyspace = keyspace; + } + + /** + * Calculates and pushes updates to the views replicas. The replicas are determined by + * {@link ViewUtils#getViewNaturalEndpoint(String, Token, Token)}. + */ + public void pushViewReplicaUpdates(PartitionUpdate update, boolean writeCommitLog) + { + List mutations = null; + TemporalRow.Set temporalRows = null; + for (Map.Entry view : viewsByName.entrySet()) + { + temporalRows = view.getValue().getTemporalRowSet(update, temporalRows, false); + + Collection viewMutations = view.getValue().createMutations(update, temporalRows, false); + if (viewMutations != null && !viewMutations.isEmpty()) + { + if (mutations == null) + mutations = Lists.newLinkedList(); + mutations.addAll(viewMutations); + } + } + + if (mutations != null) + StorageProxy.mutateMV(update.partitionKey().getKey(), mutations, writeCommitLog); + } + + public boolean updatesAffectView(Collection mutations, boolean coordinatorBatchlog) + { + if (coordinatorBatchlog && !enableCoordinatorBatchlog) + return false; + + for (IMutation mutation : mutations) + { + for (PartitionUpdate cf : mutation.getPartitionUpdates()) + { + assert keyspace.getName().equals(cf.metadata().ksName); + + if (coordinatorBatchlog && keyspace.getReplicationStrategy().getReplicationFactor() == 1) + continue; + + for (View view : allViews()) + { + if (!cf.metadata().cfId.equals(view.getDefinition().baseTableId)) + continue; + + if (view.updateAffectsView(cf)) + return true; + } + } + } + + return false; + } + + public Iterable allViews() + { + return viewsByName.values(); + } + + public void update(String viewName) + { + View view = viewsByName.get(viewName); + assert view != null : "When updating a view, it should already be in the ViewManager"; + view.build(); + + // We provide the new definition from the base metadata + Optional viewDefinition = keyspace.getMetadata().views.get(viewName); + assert viewDefinition.isPresent() : "When updating a view, it should still be in the Keyspaces views"; + view.updateDefinition(viewDefinition.get()); + } + + public void reload() + { + Map newViewsByName = new HashMap<>(); + for (ViewDefinition definition : keyspace.getMetadata().views) + { + newViewsByName.put(definition.viewName, definition); + } + + for (String viewName : viewsByName.keySet()) + { + if (!newViewsByName.containsKey(viewName)) + removeView(viewName); + } + + for (Map.Entry entry : newViewsByName.entrySet()) + { + if (!viewsByName.containsKey(entry.getKey())) + addView(entry.getValue()); + } + + for (View view : allViews()) + { + view.build(); + // We provide the new definition from the base metadata + view.updateDefinition(newViewsByName.get(view.name)); + } + } + + public void addView(ViewDefinition definition) + { + View view = new View(definition, keyspace.getColumnFamilyStore(definition.baseTableId)); + forTable(view.getDefinition().baseTableId).addView(view); + viewsByName.put(definition.viewName, view); + } + + public void removeView(String name) + { + View view = viewsByName.remove(name); + + if (view == null) + return; + + forTable(view.getDefinition().baseTableId).removeView(name); + SystemKeyspace.setViewRemoved(keyspace.getName(), view.name); + } + + public void buildAllViews() + { + for (View view : allViews()) + view.build(); + } + + public ForStore forTable(UUID baseId) + { + ForStore forStore = viewManagersByStore.get(baseId); + if (forStore == null) + { + forStore = new ForStore(); + ForStore previous = viewManagersByStore.put(baseId, forStore); + if (previous != null) + forStore = previous; + } + return forStore; + } + + public static Lock acquireLockFor(ByteBuffer key) + { + Lock lock = LOCKS.get(key); + + if (lock.tryLock()) + return lock; + + return null; + } +} diff --git a/src/java/org/apache/cassandra/db/view/MaterializedViewUtils.java b/src/java/org/apache/cassandra/db/view/ViewUtils.java similarity index 98% rename from src/java/org/apache/cassandra/db/view/MaterializedViewUtils.java rename to src/java/org/apache/cassandra/db/view/ViewUtils.java index ea817506e1..628142de98 100644 --- a/src/java/org/apache/cassandra/db/view/MaterializedViewUtils.java +++ b/src/java/org/apache/cassandra/db/view/ViewUtils.java @@ -29,9 +29,9 @@ import org.apache.cassandra.locator.AbstractReplicationStrategy; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; -public final class MaterializedViewUtils +public final class ViewUtils { - private MaterializedViewUtils() + private ViewUtils() { } diff --git a/src/java/org/apache/cassandra/dht/ByteOrderedPartitioner.java b/src/java/org/apache/cassandra/dht/ByteOrderedPartitioner.java index 46872c1429..bbf6fd6a88 100644 --- a/src/java/org/apache/cassandra/dht/ByteOrderedPartitioner.java +++ b/src/java/org/apache/cassandra/dht/ByteOrderedPartitioner.java @@ -276,7 +276,7 @@ public class ByteOrderedPartitioner implements IPartitioner for (String ks : Schema.instance.getKeyspaces()) { - for (CFMetaData cfmd : Schema.instance.getTables(ks)) + for (CFMetaData cfmd : Schema.instance.getTablesAndViews(ks)) { for (Range r : sortedRanges) { diff --git a/src/java/org/apache/cassandra/dht/OrderPreservingPartitioner.java b/src/java/org/apache/cassandra/dht/OrderPreservingPartitioner.java index 464ac3d5a2..96b4ca03df 100644 --- a/src/java/org/apache/cassandra/dht/OrderPreservingPartitioner.java +++ b/src/java/org/apache/cassandra/dht/OrderPreservingPartitioner.java @@ -216,7 +216,7 @@ public class OrderPreservingPartitioner implements IPartitioner for (String ks : Schema.instance.getKeyspaces()) { - for (CFMetaData cfmd : Schema.instance.getTables(ks)) + for (CFMetaData cfmd : Schema.instance.getTablesAndViews(ks)) { for (Range r : sortedRanges) { diff --git a/src/java/org/apache/cassandra/metrics/MVWriteMetrics.java b/src/java/org/apache/cassandra/metrics/ViewWriteMetrics.java similarity index 93% rename from src/java/org/apache/cassandra/metrics/MVWriteMetrics.java rename to src/java/org/apache/cassandra/metrics/ViewWriteMetrics.java index 39a55749a0..c99cc5c221 100644 --- a/src/java/org/apache/cassandra/metrics/MVWriteMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ViewWriteMetrics.java @@ -22,12 +22,12 @@ import com.codahale.metrics.Counter; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; -public class MVWriteMetrics extends ClientRequestMetrics +public class ViewWriteMetrics extends ClientRequestMetrics { public final Counter viewReplicasAttempted; public final Counter viewReplicasSuccess; - public MVWriteMetrics(String scope) { + public ViewWriteMetrics(String scope) { super(scope); viewReplicasAttempted = Metrics.counter(factory.createMetricName("ViewReplicasAttempted")); viewReplicasSuccess = Metrics.counter(factory.createMetricName("ViewReplicasSuccess")); diff --git a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java index 372ff6ede4..0a0d9d8d07 100644 --- a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java +++ b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java @@ -22,9 +22,12 @@ import java.util.Optional; import java.util.Set; import com.google.common.base.Objects; +import com.google.common.collect.Iterables; +import org.apache.avro.reflect.Nullable; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.exceptions.ConfigurationException; /** @@ -35,51 +38,72 @@ public final class KeyspaceMetadata public final String name; public final KeyspaceParams params; public final Tables tables; + public final Views views; public final Types types; public final Functions functions; - private KeyspaceMetadata(String name, KeyspaceParams params, Tables tables, Types types, Functions functions) + private KeyspaceMetadata(String name, KeyspaceParams params, Tables tables, Views views, Types types, Functions functions) { this.name = name; this.params = params; this.tables = tables; + this.views = views; this.types = types; this.functions = functions; } public static KeyspaceMetadata create(String name, KeyspaceParams params) { - return new KeyspaceMetadata(name, params, Tables.none(), Types.none(), Functions.none()); + return new KeyspaceMetadata(name, params, Tables.none(), Views.none(), Types.none(), Functions.none()); } public static KeyspaceMetadata create(String name, KeyspaceParams params, Tables tables) { - return new KeyspaceMetadata(name, params, tables, Types.none(), Functions.none()); + return new KeyspaceMetadata(name, params, tables, Views.none(), Types.none(), Functions.none()); } - public static KeyspaceMetadata create(String name, KeyspaceParams params, Tables tables, Types types, Functions functions) + public static KeyspaceMetadata create(String name, KeyspaceParams params, Tables tables, Views views, Types types, Functions functions) { - return new KeyspaceMetadata(name, params, tables, types, functions); + return new KeyspaceMetadata(name, params, tables, views, types, functions); } public KeyspaceMetadata withSwapped(KeyspaceParams params) { - return new KeyspaceMetadata(name, params, tables, types, functions); + return new KeyspaceMetadata(name, params, tables, views, types, functions); } - public KeyspaceMetadata withSwapped(Tables tables) + public KeyspaceMetadata withSwapped(Tables regular) { - return new KeyspaceMetadata(name, params, tables, types, functions); + return new KeyspaceMetadata(name, params, regular, views, types, functions); + } + + public KeyspaceMetadata withSwapped(Views views) + { + return new KeyspaceMetadata(name, params, tables, views, types, functions); } public KeyspaceMetadata withSwapped(Types types) { - return new KeyspaceMetadata(name, params, tables, types, functions); + return new KeyspaceMetadata(name, params, tables, views, types, functions); } public KeyspaceMetadata withSwapped(Functions functions) { - return new KeyspaceMetadata(name, params, tables, types, functions); + return new KeyspaceMetadata(name, params, tables, views, types, functions); + } + + public Iterable tablesAndViews() + { + return Iterables.concat(tables, views.metadatas()); + } + + @Nullable + public CFMetaData getTableOrViewNullable(String tableOrViewName) + { + ViewDefinition view = views.getNullable(tableOrViewName); + return view == null + ? tables.getNullable(tableOrViewName) + : view.metadata; } public Set existingIndexNames(String cfToExclude) @@ -94,7 +118,7 @@ public final class KeyspaceMetadata public Optional findIndexedTable(String indexName) { - for (CFMetaData cfm : tables) + for (CFMetaData cfm : tablesAndViews()) if (cfm.getIndexes().has(indexName)) return Optional.of(cfm); @@ -104,7 +128,7 @@ public final class KeyspaceMetadata @Override public int hashCode() { - return Objects.hashCode(name, params, tables, functions, types); + return Objects.hashCode(name, params, tables, views, functions, types); } @Override @@ -121,6 +145,7 @@ public final class KeyspaceMetadata return name.equals(other.name) && params.equals(other.params) && tables.equals(other.tables) + && views.equals(other.views) && functions.equals(other.functions) && types.equals(other.types); } @@ -132,6 +157,7 @@ public final class KeyspaceMetadata .add("name", name) .add("params", params) .add("tables", tables) + .add("views", views) .add("functions", functions) .add("types", types) .toString(); @@ -144,6 +170,6 @@ public final class KeyspaceMetadata + "or contain non-alphanumeric-underscore characters (got \"%s\")", Schema.NAME_LENGTH, name)); - tables.forEach(CFMetaData::validate); + tablesAndViews().forEach(CFMetaData::validate); } } diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java index 55b841bd95..c92261245a 100644 --- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java @@ -68,14 +68,14 @@ public final class SchemaKeyspace public static final String COLUMNS = "columns"; public static final String DROPPED_COLUMNS = "dropped_columns"; public static final String TRIGGERS = "triggers"; - public static final String MATERIALIZED_VIEWS = "materialized_views"; + public static final String VIEWS = "views"; public static final String TYPES = "types"; public static final String FUNCTIONS = "functions"; public static final String AGGREGATES = "aggregates"; public static final String INDEXES = "indexes"; public static final List ALL = - ImmutableList.of(KEYSPACES, TABLES, COLUMNS, TRIGGERS, MATERIALIZED_VIEWS, TYPES, FUNCTIONS, AGGREGATES, INDEXES); + ImmutableList.of(KEYSPACES, TABLES, COLUMNS, TRIGGERS, VIEWS, TYPES, FUNCTIONS, AGGREGATES, INDEXES); private static final CFMetaData Keyspaces = compile(KEYSPACES, @@ -144,17 +144,31 @@ public final class SchemaKeyspace + "options frozen>," + "PRIMARY KEY ((keyspace_name), table_name, trigger_name))"); - private static final CFMetaData MaterializedViews = - compile(MATERIALIZED_VIEWS, - "materialized views definitions", + private static final CFMetaData Views = + compile(VIEWS, + "view definitions", "CREATE TABLE %s (" + "keyspace_name text," - + "table_name text," + "view_name text," - + "target_columns frozen>," - + "clustering_columns frozen>," - + "included_columns frozen>," - + "PRIMARY KEY ((keyspace_name), table_name, view_name))"); + + "base_table_id uuid," + + "base_table_name text," + + "bloom_filter_fp_chance double," + + "caching frozen>," + + "comment text," + + "compaction frozen>," + + "compression frozen>," + + "dclocal_read_repair_chance double," + + "default_time_to_live int," + + "extensions frozen>," + + "gc_grace_seconds int," + + "id uuid," + + "include_all_columns boolean," + + "max_index_interval int," + + "memtable_flush_period_in_ms int," + + "min_index_interval int," + + "read_repair_chance double," + + "speculative_retry text," + + "PRIMARY KEY ((keyspace_name), view_name))"); private static final CFMetaData Indexes = compile(INDEXES, @@ -210,7 +224,7 @@ public final class SchemaKeyspace + "PRIMARY KEY ((keyspace_name), aggregate_name, signature))"); public static final List ALL_TABLE_METADATA = - ImmutableList.of(Keyspaces, Tables, Columns, Triggers, DroppedColumns, MaterializedViews, Types, Functions, Aggregates, Indexes); + ImmutableList.of(Keyspaces, Tables, Columns, Triggers, DroppedColumns, Views, Types, Functions, Aggregates, Indexes); private static CFMetaData compile(String name, String description, String schema) { @@ -265,9 +279,10 @@ public final class SchemaKeyspace readSchemaPartitionForKeyspaceAndApply(TYPES, key, types -> readSchemaPartitionForKeyspaceAndApply(TABLES, key, - tables -> readSchemaPartitionForKeyspaceAndApply(FUNCTIONS, key, + tables -> readSchemaPartitionForKeyspaceAndApply(VIEWS, key, + views -> readSchemaPartitionForKeyspaceAndApply(FUNCTIONS, key, functions -> readSchemaPartitionForKeyspaceAndApply(AGGREGATES, key, - aggregates -> keyspaces.add(createKeyspaceFromSchemaPartitions(partition, tables, types, functions, aggregates))))) + aggregates -> keyspaces.add(createKeyspaceFromSchemaPartitions(partition, tables, views, types, functions, aggregates)))))) ); } } @@ -473,6 +488,7 @@ public final class SchemaKeyspace // current state of the schema Map oldKeyspaces = readSchemaForKeyspaces(KEYSPACES, keyspaces); Map oldColumnFamilies = readSchemaForKeyspaces(TABLES, keyspaces); + Map oldViews = readSchemaForKeyspaces(VIEWS, keyspaces); Map oldTypes = readSchemaForKeyspaces(TYPES, keyspaces); Map oldFunctions = readSchemaForKeyspaces(FUNCTIONS, keyspaces); Map oldAggregates = readSchemaForKeyspaces(AGGREGATES, keyspaces); @@ -485,12 +501,14 @@ public final class SchemaKeyspace // with new data applied Map newKeyspaces = readSchemaForKeyspaces(KEYSPACES, keyspaces); Map newColumnFamilies = readSchemaForKeyspaces(TABLES, keyspaces); + Map newViews = readSchemaForKeyspaces(VIEWS, keyspaces); Map newTypes = readSchemaForKeyspaces(TYPES, keyspaces); Map newFunctions = readSchemaForKeyspaces(FUNCTIONS, keyspaces); Map newAggregates = readSchemaForKeyspaces(AGGREGATES, keyspaces); Set keyspacesToDrop = mergeKeyspaces(oldKeyspaces, newKeyspaces); mergeTables(oldColumnFamilies, newColumnFamilies); + mergeViews(oldViews, newViews); mergeTypes(oldTypes, newTypes); mergeFunctions(oldFunctions, newFunctions); mergeAggregates(oldAggregates, newAggregates); @@ -546,6 +564,27 @@ public final class SchemaKeyspace }); } + private static void mergeViews(Map before, Map after) + { + diffSchema(before, after, new Differ() + { + public void onDropped(UntypedResultSet.Row oldRow) + { + Schema.instance.dropView(oldRow.getString("keyspace_name"), oldRow.getString("view_name")); + } + + public void onAdded(UntypedResultSet.Row newRow) + { + Schema.instance.addView(createViewFromViewRow(newRow)); + } + + public void onUpdated(UntypedResultSet.Row oldRow, UntypedResultSet.Row newRow) + { + Schema.instance.updateView(newRow.getString("keyspace_name"), newRow.getString("view_name")); + } + }); + } + private static void mergeTypes(Map before, Map after) { diffSchema(before, after, new Differ() @@ -697,6 +736,7 @@ public final class SchemaKeyspace Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation)); + keyspace.views.forEach(view -> addViewToSchemaMutation(view, timestamp, true, mutation)); keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation)); keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation)); keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation)); @@ -717,6 +757,7 @@ public final class SchemaKeyspace private static KeyspaceMetadata createKeyspaceFromSchemaPartitions(RowIterator serializedParams, RowIterator serializedTables, + RowIterator serializedViews, RowIterator serializedTypes, RowIterator serializedFunctions, RowIterator serializedAggregates) @@ -725,13 +766,14 @@ public final class SchemaKeyspace KeyspaceParams params = createKeyspaceParamsFromSchemaPartition(serializedParams); Tables tables = createTablesFromTablesPartition(serializedTables); + Views views = createViewsFromViewsPartition(serializedViews); Types types = createTypesFromPartition(serializedTypes); Collection udfs = createFunctionsFromFunctionsPartition(serializedFunctions); Collection udas = createAggregatesFromAggregatesPartition(serializedAggregates); Functions functions = org.apache.cassandra.schema.Functions.builder().add(udfs).add(udas).build(); - return KeyspaceMetadata.create(name, params, tables, types, functions); + return KeyspaceMetadata.create(name, params, tables, views, types, functions); } /** @@ -849,9 +891,6 @@ public final class SchemaKeyspace for (TriggerMetadata trigger : table.getTriggers()) addTriggerToSchemaMutation(table, trigger, timestamp, mutation); - for (MaterializedViewDefinition materializedView: table.getMaterializedViews()) - addMaterializedViewToSchemaMutation(table, materializedView, timestamp, mutation); - for (IndexMetadata index : table.getIndexes()) addIndexToSchemaMutation(table, index, timestamp, mutation); } @@ -931,22 +970,6 @@ public final class SchemaKeyspace for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnRight().values()) addTriggerToSchemaMutation(newTable, trigger, timestamp, mutation); - MapDifference materializedViewDiff = materializedViewsDiff(oldTable.getMaterializedViews(), newTable.getMaterializedViews()); - - // dropped materialized views - for (MaterializedViewDefinition materializedView : materializedViewDiff.entriesOnlyOnLeft().values()) - dropMaterializedViewFromSchemaMutation(oldTable, materializedView, timestamp, mutation); - - // newly created materialized views - for (MaterializedViewDefinition materializedView : materializedViewDiff.entriesOnlyOnRight().values()) - addMaterializedViewToSchemaMutation(newTable, materializedView, timestamp, mutation); - - // updated materialized views need to be updated - for (MapDifference.ValueDifference diff : materializedViewDiff.entriesDiffering().values()) - { - addUpdatedMaterializedViewDefinitionToSchemaMutation(newTable, diff.rightValue(), timestamp, mutation); - } - MapDifference indexesDiff = indexesDiff(oldTable.getIndexes(), newTable.getIndexes()); @@ -989,17 +1012,6 @@ public final class SchemaKeyspace return Maps.difference(beforeMap, afterMap); } - private static MapDifference materializedViewsDiff(MaterializedViews before, MaterializedViews after) - { - Map beforeMap = new HashMap<>(); - before.forEach(v -> beforeMap.put(v.viewName, v)); - - Map afterMap = new HashMap<>(); - after.forEach(v -> afterMap.put(v.viewName, v)); - - return Maps.difference(beforeMap, afterMap); - } - public static Mutation makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp) { // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). @@ -1013,9 +1025,6 @@ public final class SchemaKeyspace for (TriggerMetadata trigger : table.getTriggers()) dropTriggerFromSchemaMutation(table, trigger, timestamp, mutation); - for (MaterializedViewDefinition materializedView : table.getMaterializedViews()) - dropMaterializedViewFromSchemaMutation(table, materializedView, timestamp, mutation); - for (IndexMetadata index : table.getIndexes()) dropIndexFromSchemaMutation(table, index, timestamp, mutation); @@ -1083,12 +1092,8 @@ public final class SchemaKeyspace Triggers triggers = readSchemaPartitionForTableAndApply(TRIGGERS, keyspace, table, SchemaKeyspace::createTriggersFromTriggersPartition); - MaterializedViews views = - readSchemaPartitionForTableAndApply(MATERIALIZED_VIEWS, keyspace, table, SchemaKeyspace::createMaterializedViewsFromMaterializedViewsPartition); - CFMetaData cfm = createTableFromTableRowAndColumns(row, columns).droppedColumns(droppedColumns) - .triggers(triggers) - .materializedViews(views); + .triggers(triggers); // the CFMetaData itself is required to build the collection of indexes as // the column definitions are needed because we store only the name each @@ -1114,7 +1119,6 @@ public final class SchemaKeyspace boolean isCounter = flags.contains(CFMetaData.Flag.COUNTER); boolean isDense = flags.contains(CFMetaData.Flag.DENSE); boolean isCompound = flags.contains(CFMetaData.Flag.COMPOUND); - boolean isMaterializedView = flags.contains(CFMetaData.Flag.VIEW); return CFMetaData.create(keyspace, table, @@ -1123,7 +1127,7 @@ public final class SchemaKeyspace isCompound, isSuper, isCounter, - isMaterializedView, + false, columns, DatabaseDescriptor.getPartitioner()) .params(createTableParamsFromRow(row)); @@ -1274,82 +1278,164 @@ public final class SchemaKeyspace } /* - * Materialized View metadata serialization/deserialization. + * View metadata serialization/deserialization. */ - private static void addMaterializedViewToSchemaMutation(CFMetaData table, MaterializedViewDefinition materializedView, long timestamp, Mutation mutation) + public static Mutation makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp) { - RowUpdateBuilder builder = new RowUpdateBuilder(MaterializedViews, timestamp, mutation) - .clustering(table.cfName, materializedView.viewName); + // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). + Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); + addViewToSchemaMutation(view, timestamp, true, mutation); + return mutation; + } - builder.frozenList("target_columns", materializedView.partitionColumns.stream().map(ColumnIdentifier::toString).collect(Collectors.toList())); - builder.frozenList("clustering_columns", materializedView.clusteringColumns.stream().map(ColumnIdentifier::toString).collect(Collectors.toList())); - builder.frozenList("included_columns", materializedView.included.stream().map(ColumnIdentifier::toString).collect(Collectors.toList())); + private static void addViewToSchemaMutation(ViewDefinition view, long timestamp, boolean includeColumns, Mutation mutation) + { + RowUpdateBuilder builder = new RowUpdateBuilder(Views, timestamp, mutation) + .clustering(view.viewName); + + CFMetaData table = view.metadata; + + builder.add("include_all_columns", view.includeAllColumns) + .add("base_table_id", view.baseTableId) + .add("base_table_name", view.baseTableMetadata().cfName) + .add("id", table.cfId); + + addTableParamsToSchemaMutation(table.params, builder); + + if (includeColumns) + { + for (ColumnDefinition column : table.allColumns()) + addColumnToSchemaMutation(table, column, timestamp, mutation); + + for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values()) + addDroppedColumnToSchemaMutation(table, column, timestamp, mutation); + } builder.build(); } - private static void dropMaterializedViewFromSchemaMutation(CFMetaData table, MaterializedViewDefinition materializedView, long timestamp, Mutation mutation) + public static Mutation makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp) { - RowUpdateBuilder.deleteRow(MaterializedViews, timestamp, mutation, table.cfName, materializedView.viewName); + // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631). + Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); + + RowUpdateBuilder.deleteRow(Views, timestamp, mutation, view.viewName); + + CFMetaData table = view.metadata; + for (ColumnDefinition column : table.allColumns()) + dropColumnFromSchemaMutation(table, column, timestamp, mutation); + + for (IndexMetadata index : table.getIndexes()) + dropIndexFromSchemaMutation(table, index, timestamp, mutation); + + return mutation; } - private static void addUpdatedMaterializedViewDefinitionToSchemaMutation(CFMetaData table, MaterializedViewDefinition materializedView, long timestamp, Mutation mutation) + public static Mutation makeUpdateViewMutation(KeyspaceMetadata keyspace, + ViewDefinition oldView, + ViewDefinition newView, + long timestamp) { - addMaterializedViewToSchemaMutation(table, materializedView, timestamp, mutation); + Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp); + + addViewToSchemaMutation(newView, timestamp, false, mutation); + + MapDifference columnDiff = Maps.difference(oldView.metadata.getColumnMetadata(), + newView.metadata.getColumnMetadata()); + + // columns that are no longer needed + for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values()) + { + dropColumnFromSchemaMutation(oldView.metadata, column, timestamp, mutation); + } + + // newly added columns + for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values()) + addColumnToSchemaMutation(newView.metadata, column, timestamp, mutation); + + // old columns with updated attributes + for (ByteBuffer name : columnDiff.entriesDiffering().keySet()) + addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), timestamp, mutation); + + // dropped columns + MapDifference droppedColumnDiff = + Maps.difference(oldView.metadata.getDroppedColumns(), oldView.metadata.getDroppedColumns()); + + // newly dropped columns + for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values()) + addDroppedColumnToSchemaMutation(oldView.metadata, column, timestamp, mutation); + + // columns added then dropped again + for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet()) + addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), timestamp, mutation); + + return mutation; + } + + public static ViewDefinition createViewFromName(String keyspace, String view) + { + return readSchemaPartitionForTableAndApply(VIEWS, keyspace, view, partition -> + { + if (partition.isEmpty()) + throw new RuntimeException(String.format("%s:%s not found in the schema definitions keyspace.", keyspace, view)); + + return createViewFromViewPartition(partition); + }); + } + + private static ViewDefinition createViewFromViewPartition(RowIterator partition) + { + String query = String.format("SELECT * FROM %s.%s", NAME, VIEWS); + return createViewFromViewRow(QueryProcessor.resultify(query, partition).one()); } /** - * Deserialize materialized views from storage-level representation. + * Deserialize views from storage-level representation. * - * @param partition storage-level partition containing the materialized view definitions - * @return the list of processed MaterializedViewDefinitions + * @param partition storage-level partition containing the view definitions + * @return the list of processed ViewDefinitions */ - private static MaterializedViews createMaterializedViewsFromMaterializedViewsPartition(RowIterator partition) + private static Views createViewsFromViewsPartition(RowIterator partition) { - MaterializedViews.Builder views = org.apache.cassandra.schema.MaterializedViews.builder(); - String query = String.format("SELECT * FROM %s.%s", NAME, MATERIALIZED_VIEWS); + Views.Builder views = org.apache.cassandra.schema.Views.builder(); + String query = String.format("SELECT * FROM %s.%s", NAME, VIEWS); for (UntypedResultSet.Row row : QueryProcessor.resultify(query, partition)) { - MaterializedViewDefinition mv = createMaterializedViewFromMaterializedViewRow(row); - views.add(mv); + ViewDefinition view = createViewFromViewRow(row); + views.add(view); } return views.build(); } - private static MaterializedViewDefinition createMaterializedViewFromMaterializedViewRow(UntypedResultSet.Row row) + private static ViewDefinition createViewFromViewRow(UntypedResultSet.Row row) { - String name = row.getString("view_name"); - List partitionColumnNames = row.getFrozenList("target_columns", UTF8Type.instance); + String keyspace = row.getString("keyspace_name"); + String view = row.getString("view_name"); + UUID id = row.getUUID("id"); + UUID baseTableId = row.getUUID("base_table_id"); + boolean includeAll = row.getBoolean("include_all_columns"); - String cfName = row.getString("table_name"); - List clusteringColumnNames = row.getFrozenList("clustering_columns", UTF8Type.instance); + List columns = + readSchemaPartitionForTableAndApply(COLUMNS, keyspace, view, SchemaKeyspace::createColumnsFromColumnsPartition); - List partitionColumns = new ArrayList<>(); - for (String columnName : partitionColumnNames) - { - partitionColumns.add(ColumnIdentifier.getInterned(columnName, true)); - } + Map droppedColumns = + readSchemaPartitionForTableAndApply(DROPPED_COLUMNS, keyspace, view, SchemaKeyspace::createDroppedColumnsFromDroppedColumnsPartition); - List clusteringColumns = new ArrayList<>(); - for (String columnName : clusteringColumnNames) - { - clusteringColumns.add(ColumnIdentifier.getInterned(columnName, true)); - } + CFMetaData cfm = CFMetaData.create(keyspace, + view, + id, + false, + true, + false, + false, + true, + columns, + DatabaseDescriptor.getPartitioner()) + .params(createTableParamsFromRow(row)) + .droppedColumns(droppedColumns); - List includedColumnNames = row.getFrozenList("included_columns", UTF8Type.instance); - Set includedColumns = new HashSet<>(); - if (includedColumnNames != null) - { - for (String columnName : includedColumnNames) - includedColumns.add(ColumnIdentifier.getInterned(columnName, true)); - } - - return new MaterializedViewDefinition(cfName, - name, - partitionColumns, - clusteringColumns, - includedColumns); + return new ViewDefinition(keyspace, view, baseTableId, includeAll, cfm); } /* diff --git a/src/java/org/apache/cassandra/schema/MaterializedViews.java b/src/java/org/apache/cassandra/schema/Views.java similarity index 51% rename from src/java/org/apache/cassandra/schema/MaterializedViews.java rename to src/java/org/apache/cassandra/schema/Views.java index 1c55736050..5888b9dc93 100644 --- a/src/java/org/apache/cassandra/schema/MaterializedViews.java +++ b/src/java/org/apache/cassandra/schema/Views.java @@ -22,19 +22,23 @@ package org.apache.cassandra.schema; import java.util.Iterator; import java.util.Optional; -import com.google.common.collect.ImmutableMap; +import javax.annotation.Nullable; -import org.apache.cassandra.config.MaterializedViewDefinition; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.ViewDefinition; import static com.google.common.collect.Iterables.filter; -public final class MaterializedViews implements Iterable +public final class Views implements Iterable { - private final ImmutableMap materializedViews; + private final ImmutableMap views; - private MaterializedViews(Builder builder) + private Views(Builder builder) { - materializedViews = builder.materializedViews.build(); + views = builder.views.build(); } public static Builder builder() @@ -42,55 +46,72 @@ public final class MaterializedViews implements Iterable iterator() + public Iterator iterator() { - return materializedViews.values().iterator(); + return views.values().iterator(); + } + + public Iterable metadatas() + { + return Iterables.transform(views.values(), view -> view.metadata); } public int size() { - return materializedViews.size(); + return views.size(); } public boolean isEmpty() { - return materializedViews.isEmpty(); + return views.isEmpty(); } /** * Get the materialized view with the specified name * * @param name a non-qualified materialized view name - * @return an empty {@link Optional} if the materialized view name is not found; a non-empty optional of {@link MaterializedViewDefinition} otherwise + * @return an empty {@link Optional} if the materialized view name is not found; a non-empty optional of {@link ViewDefinition} otherwise */ - public Optional get(String name) + public Optional get(String name) { - return Optional.ofNullable(materializedViews.get(name)); + return Optional.ofNullable(views.get(name)); + } + + /** + * Get the view with the specified name + * + * @param name a non-qualified view name + * @return null if the view name is not found; the found {@link ViewDefinition} otherwise + */ + @Nullable + public ViewDefinition getNullable(String name) + { + return views.get(name); } /** * Create a MaterializedViews instance with the provided materialized view added */ - public MaterializedViews with(MaterializedViewDefinition materializedView) + public Views with(ViewDefinition view) { - if (get(materializedView.viewName).isPresent()) - throw new IllegalStateException(String.format("Materialized View %s already exists", materializedView.viewName)); + if (get(view.viewName).isPresent()) + throw new IllegalStateException(String.format("Materialized View %s already exists", view.viewName)); - return builder().add(this).add(materializedView).build(); + return builder().add(this).add(view).build(); } /** * Creates a MaterializedViews instance with the materializedView with the provided name removed */ - public MaterializedViews without(String name) + public Views without(String name) { - MaterializedViewDefinition materializedView = - get(name).orElseThrow(() -> new IllegalStateException(String.format("Materialized View %s doesn't exists", name))); + ViewDefinition materializedView = + get(name).orElseThrow(() -> new IllegalStateException(String.format("Materialized View %s doesn't exists", name))); return builder().add(filter(this, v -> v != materializedView)).build(); } @@ -98,51 +119,52 @@ public final class MaterializedViews implements Iterable materializedViews = new ImmutableMap.Builder<>(); + final ImmutableMap.Builder views = new ImmutableMap.Builder<>(); private Builder() { } - public MaterializedViews build() + public Views build() { - return new MaterializedViews(this); + return new Views(this); } - public Builder add(MaterializedViewDefinition materializedView) + + public Builder add(ViewDefinition view) { - materializedViews.put(materializedView.viewName, materializedView); + views.put(view.viewName, view); return this; } - public Builder add(Iterable materializedViews) + public Builder add(Iterable views) { - materializedViews.forEach(this::add); + views.forEach(this::add); return this; } } diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index 230b46a693..1408a70ce1 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -230,7 +230,7 @@ public class CassandraDaemon if (keyspaceName.equals(SystemKeyspace.NAME)) continue; - for (CFMetaData cfm : Schema.instance.getTables(keyspaceName)) + for (CFMetaData cfm : Schema.instance.getTablesAndViews(keyspaceName)) ColumnFamilyStore.scrubDataDirectories(cfm); } @@ -295,22 +295,19 @@ public class CassandraDaemon } } - Runnable indexRebuild = new Runnable() + Runnable viewRebuild = new Runnable() { @Override public void run() { for (Keyspace keyspace : Keyspace.all()) { - for (ColumnFamilyStore cf: keyspace.getColumnFamilyStores()) - { - cf.materializedViewManager.buildAllViews(); - } + keyspace.viewManager.buildAllViews(); } } }; - ScheduledExecutors.optionalTasks.schedule(indexRebuild, StorageService.RING_DELAY, TimeUnit.MILLISECONDS); + ScheduledExecutors.optionalTasks.schedule(viewRebuild, StorageService.RING_DELAY, TimeUnit.MILLISECONDS); SystemKeyspace.finishStartup(); diff --git a/src/java/org/apache/cassandra/service/MigrationListener.java b/src/java/org/apache/cassandra/service/MigrationListener.java index 358b2367c7..f4b3e7cdad 100644 --- a/src/java/org/apache/cassandra/service/MigrationListener.java +++ b/src/java/org/apache/cassandra/service/MigrationListener.java @@ -31,6 +31,11 @@ public abstract class MigrationListener { } + public void onCreateView(String ksName, String viewName) + { + onCreateColumnFamily(ksName, viewName); + } + public void onCreateUserType(String ksName, String typeName) { } @@ -51,6 +56,11 @@ public abstract class MigrationListener { } + public void onUpdateView(String ksName, String viewName, boolean columnsDidChange) + { + onUpdateColumnFamily(ksName, viewName, columnsDidChange); + } + public void onUpdateUserType(String ksName, String typeName) { } @@ -71,6 +81,10 @@ public abstract class MigrationListener { } + public void onDropView(String ksName, String viewName) + { + } + public void onDropUserType(String ksName, String typeName) { } diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java index dad6aa7cf6..c820f18a39 100644 --- a/src/java/org/apache/cassandra/service/MigrationManager.java +++ b/src/java/org/apache/cassandra/service/MigrationManager.java @@ -31,6 +31,7 @@ import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.concurrent.StageManager; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.ViewDefinition; import org.apache.cassandra.cql3.functions.UDAggregate; import org.apache.cassandra.cql3.functions.UDFunction; import org.apache.cassandra.db.*; @@ -162,6 +163,12 @@ public class MigrationManager listener.onCreateColumnFamily(cfm.ksName, cfm.cfName); } + public void notifyCreateView(ViewDefinition view) + { + for (MigrationListener listener : listeners) + listener.onCreateView(view.ksName, view.viewName); + } + public void notifyCreateUserType(UserType ut) { for (MigrationListener listener : listeners) @@ -192,6 +199,12 @@ public class MigrationManager listener.onUpdateColumnFamily(cfm.ksName, cfm.cfName, columnsDidChange); } + public void notifyUpdateView(ViewDefinition view, boolean columnsDidChange) + { + for (MigrationListener listener : listeners) + listener.onUpdateView(view.ksName, view.viewName, columnsDidChange); + } + public void notifyUpdateUserType(UserType ut) { for (MigrationListener listener : listeners) @@ -225,6 +238,12 @@ public class MigrationManager listener.onDropColumnFamily(cfm.ksName, cfm.cfName); } + public void notifyDropView(ViewDefinition view) + { + for (MigrationListener listener : listeners) + listener.onDropView(view.ksName, view.viewName); + } + public void notifyDropUserType(UserType ut) { for (MigrationListener listener : listeners) @@ -276,13 +295,28 @@ public class MigrationManager KeyspaceMetadata ksm = Schema.instance.getKSMetaData(cfm.ksName); if (ksm == null) throw new ConfigurationException(String.format("Cannot add table '%s' to non existing keyspace '%s'.", cfm.cfName, cfm.ksName)); - else if (ksm.tables.get(cfm.cfName).isPresent()) + // If we have a table or a view which has the same name, we can't add a new one + else if (ksm.getTableOrViewNullable(cfm.cfName) != null) throw new AlreadyExistsException(cfm.ksName, cfm.cfName); logger.info(String.format("Create new table: %s", cfm)); announce(SchemaKeyspace.makeCreateTableMutation(ksm, cfm, FBUtilities.timestampMicros()), announceLocally); } + public static void announceNewView(ViewDefinition view, boolean announceLocally) throws ConfigurationException + { + view.metadata.validate(); + + KeyspaceMetadata ksm = Schema.instance.getKSMetaData(view.ksName); + if (ksm == null) + throw new ConfigurationException(String.format("Cannot add table '%s' to non existing keyspace '%s'.", view.viewName, view.ksName)); + else if (ksm.getTableOrViewNullable(view.viewName) != null) + throw new AlreadyExistsException(view.ksName, view.viewName); + + logger.info(String.format("Create new view: %s", view)); + announce(SchemaKeyspace.makeCreateViewMutation(ksm, view, FBUtilities.timestampMicros()), announceLocally); + } + public static void announceNewType(UserType newType, boolean announceLocally) { KeyspaceMetadata ksm = Schema.instance.getKSMetaData(newType.keyspace); @@ -340,6 +374,21 @@ public class MigrationManager announce(SchemaKeyspace.makeUpdateTableMutation(ksm, oldCfm, cfm, FBUtilities.timestampMicros(), fromThrift), announceLocally); } + public static void announceViewUpdate(ViewDefinition view, boolean announceLocally) throws ConfigurationException + { + view.metadata.validate(); + + ViewDefinition oldView = Schema.instance.getView(view.ksName, view.viewName); + if (oldView == null) + throw new ConfigurationException(String.format("Cannot update non existing materialized view '%s' in keyspace '%s'.", view.viewName, view.ksName)); + KeyspaceMetadata ksm = Schema.instance.getKSMetaData(view.ksName); + + oldView.metadata.validateCompatility(view.metadata); + + logger.info(String.format("Update view '%s/%s' From %s To %s", view.ksName, view.viewName, oldView, view)); + announce(SchemaKeyspace.makeUpdateViewMutation(ksm, oldView, view, FBUtilities.timestampMicros()), announceLocally); + } + public static void announceTypeUpdate(UserType updatedType, boolean announceLocally) { announceNewType(updatedType, announceLocally); @@ -376,6 +425,17 @@ public class MigrationManager announce(SchemaKeyspace.makeDropTableMutation(ksm, oldCfm, FBUtilities.timestampMicros()), announceLocally); } + public static void announceViewDrop(String ksName, String viewName, boolean announceLocally) throws ConfigurationException + { + ViewDefinition view = Schema.instance.getView(ksName, viewName); + if (view == null) + throw new ConfigurationException(String.format("Cannot drop non existing materialized view '%s' in keyspace '%s'.", viewName, ksName)); + KeyspaceMetadata ksm = Schema.instance.getKSMetaData(ksName); + + logger.info(String.format("Drop table '%s/%s'", view.ksName, view.viewName)); + announce(SchemaKeyspace.makeDropViewMutation(ksm, view, FBUtilities.timestampMicros()), announceLocally); + } + public static void announceTypeDrop(UserType droppedType) { announceTypeDrop(droppedType, false); diff --git a/src/java/org/apache/cassandra/service/StartupChecks.java b/src/java/org/apache/cassandra/service/StartupChecks.java index eca0c7eecc..16ff488078 100644 --- a/src/java/org/apache/cassandra/service/StartupChecks.java +++ b/src/java/org/apache/cassandra/service/StartupChecks.java @@ -270,7 +270,7 @@ public class StartupChecks // we do a one-off scrub of the system keyspace first; we can't load the list of the rest of the keyspaces, // until system keyspace is opened. - for (CFMetaData cfm : Schema.instance.getTables(SystemKeyspace.NAME)) + for (CFMetaData cfm : Schema.instance.getTablesAndViews(SystemKeyspace.NAME)) ColumnFamilyStore.scrubDataDirectories(cfm); try diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index e3b884e797..d209af6b7d 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -48,12 +48,9 @@ import org.apache.cassandra.db.filter.DataLimits; import org.apache.cassandra.db.filter.TombstoneOverwhelmingException; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.db.rows.RowIterator; -import org.apache.cassandra.db.view.MaterializedViewManager; -import org.apache.cassandra.db.view.MaterializedViewUtils; -import org.apache.cassandra.dht.AbstractBounds; -import org.apache.cassandra.dht.Bounds; -import org.apache.cassandra.dht.RingPosition; -import org.apache.cassandra.dht.Token; +import org.apache.cassandra.db.view.ViewManager; +import org.apache.cassandra.db.view.ViewUtils; +import org.apache.cassandra.dht.*; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.gms.FailureDetector; import org.apache.cassandra.gms.Gossiper; @@ -98,7 +95,7 @@ public class StorageProxy implements StorageProxyMBean private static final ClientRequestMetrics writeMetrics = new ClientRequestMetrics("Write"); private static final CASClientRequestMetrics casWriteMetrics = new CASClientRequestMetrics("CASWrite"); private static final CASClientRequestMetrics casReadMetrics = new CASClientRequestMetrics("CASRead"); - private static final MVWriteMetrics mvWriteMetrics = new MVWriteMetrics("MVWrite"); + private static final ViewWriteMetrics viewWriteMetrics = new ViewWriteMetrics("ViewWrite"); private static final double CONCURRENT_SUBREQUESTS_MARGIN = 0.10; @@ -655,6 +652,7 @@ public class StorageProxy implements StorageProxyMBean * @param mutations the mutations to be applied across the replicas */ public static void mutateMV(ByteBuffer dataKey, Collection mutations, boolean writeCommitLog) + throws UnavailableException, OverloadedException, WriteTimeoutException { Tracing.trace("Determining replicas for mutation"); final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); @@ -679,15 +677,15 @@ public class StorageProxy implements StorageProxyMBean { String keyspaceName = mutation.getKeyspaceName(); Token tk = mutation.key().getToken(); - InetAddress pairedEndpoint = MaterializedViewUtils.getViewNaturalEndpoint(keyspaceName, baseToken, tk); + InetAddress pairedEndpoint = ViewUtils.getViewNaturalEndpoint(keyspaceName, baseToken, tk); List naturalEndpoints = Lists.newArrayList(pairedEndpoint); - WriteResponseHandlerWrapper wrapper = wrapMVBatchResponseHandler(mutation, - consistencyLevel, - consistencyLevel, - naturalEndpoints, - WriteType.BATCH, - cleanup); + WriteResponseHandlerWrapper wrapper = wrapViewBatchResponseHandler(mutation, + consistencyLevel, + consistencyLevel, + naturalEndpoints, + WriteType.BATCH, + cleanup); // When local node is the endpoint and there are no pending nodes we can // Just apply the mutation locally. @@ -704,12 +702,12 @@ public class StorageProxy implements StorageProxyMBean writeCommitLog); // now actually perform the writes and wait for them to complete - asyncWriteBatchedMutations(wrappers, localDataCenter, Stage.MATERIALIZED_VIEW_MUTATION); + asyncWriteBatchedMutations(wrappers, localDataCenter, Stage.VIEW_MUTATION); } } finally { - mvWriteMetrics.addNano(System.nanoTime() - startTime); + viewWriteMetrics.addNano(System.nanoTime() - startTime); } } @@ -721,7 +719,9 @@ public class StorageProxy implements StorageProxyMBean { Collection augmented = TriggerExecutor.instance.execute(mutations); - boolean updatesView = MaterializedViewManager.updatesAffectView(mutations, true); + boolean updatesView = Keyspace.open(mutations.iterator().next().getKeyspaceName()) + .viewManager + .updatesAffectView(mutations, true); if (augmented != null) mutateAtomically(augmented, consistencyLevel, updatesView); @@ -974,14 +974,14 @@ public class StorageProxy implements StorageProxyMBean /** * Same as performWrites except does not initiate writes (but does perform availability checks). - * Keeps track of MVWriteMetrics + * Keeps track of ViewWriteMetrics */ - private static WriteResponseHandlerWrapper wrapMVBatchResponseHandler(Mutation mutation, - ConsistencyLevel consistency_level, - ConsistencyLevel batchConsistencyLevel, - List naturalEndpoints, - WriteType writeType, - BatchlogResponseHandler.BatchlogCleanup cleanup) + private static WriteResponseHandlerWrapper wrapViewBatchResponseHandler(Mutation mutation, + ConsistencyLevel consistency_level, + ConsistencyLevel batchConsistencyLevel, + List naturalEndpoints, + WriteType writeType, + BatchlogResponseHandler.BatchlogCleanup cleanup) { Keyspace keyspace = Keyspace.open(mutation.getKeyspaceName()); AbstractReplicationStrategy rs = keyspace.getReplicationStrategy(); @@ -989,7 +989,7 @@ public class StorageProxy implements StorageProxyMBean Token tk = mutation.key().getToken(); Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); AbstractWriteResponseHandler writeHandler = rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, null, writeType); - BatchlogResponseHandler batchHandler = new MVWriteMetricsWrapped(writeHandler, batchConsistencyLevel.blockFor(keyspace), cleanup); + BatchlogResponseHandler batchHandler = new ViewWriteMetricsWrapped(writeHandler, batchConsistencyLevel.blockFor(keyspace), cleanup); return new WriteResponseHandlerWrapper(batchHandler, mutation); } @@ -2282,20 +2282,20 @@ public class StorageProxy implements StorageProxyMBean } /** - * This class captures metrics for materialized views writes. + * This class captures metrics for views writes. */ - private static class MVWriteMetricsWrapped extends BatchlogResponseHandler + private static class ViewWriteMetricsWrapped extends BatchlogResponseHandler { - public MVWriteMetricsWrapped(AbstractWriteResponseHandler writeHandler, int i, BatchlogCleanup cleanup) + public ViewWriteMetricsWrapped(AbstractWriteResponseHandler writeHandler, int i, BatchlogCleanup cleanup) { super(writeHandler, i, cleanup); - mvWriteMetrics.viewReplicasAttempted.inc(totalEndpoints()); + viewWriteMetrics.viewReplicasAttempted.inc(totalEndpoints()); } public void response(MessageIn msg) { super.response(msg); - mvWriteMetrics.viewReplicasSuccess.inc(); + viewWriteMetrics.viewReplicasSuccess.inc(); } } diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index f09563015e..f0ad46ffa5 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -630,12 +630,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public void runMayThrow() throws InterruptedException { inShutdownHook = true; - ExecutorService materializedViewMutationStage = StageManager.getStage(Stage.MATERIALIZED_VIEW_MUTATION); + ExecutorService viewMutationStage = StageManager.getStage(Stage.VIEW_MUTATION); ExecutorService counterMutationStage = StageManager.getStage(Stage.COUNTER_MUTATION); ExecutorService mutationStage = StageManager.getStage(Stage.MUTATION); if (mutationStage.isShutdown() && counterMutationStage.isShutdown() - && materializedViewMutationStage.isShutdown()) + && viewMutationStage.isShutdown()) return; // drained already if (daemon != null) @@ -646,11 +646,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // In-progress writes originating here could generate hints to be written, so shut down MessagingService // before mutation stage, so we can get all the hints saved before shutting down MessagingService.instance().shutdown(); - materializedViewMutationStage.shutdown(); + viewMutationStage.shutdown(); HintsService.instance.pauseDispatch(); counterMutationStage.shutdown(); mutationStage.shutdown(); - materializedViewMutationStage.awaitTermination(3600, TimeUnit.SECONDS); + viewMutationStage.awaitTermination(3600, TimeUnit.SECONDS); counterMutationStage.awaitTermination(3600, TimeUnit.SECONDS); mutationStage.awaitTermination(3600, TimeUnit.SECONDS); StorageProxy.instance.verifyNoHintsInProgress(); @@ -3179,7 +3179,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (ksMetaData == null) throw new IllegalArgumentException("Unknown keyspace '" + keyspaceName + "'"); - CFMetaData cfMetaData = ksMetaData.tables.getNullable(cf); + CFMetaData cfMetaData = ksMetaData.getTableOrViewNullable(cf); if (cfMetaData == null) throw new IllegalArgumentException("Unknown table '" + cf + "' in keyspace '" + keyspaceName + "'"); @@ -3876,11 +3876,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE HintsService.instance.pauseDispatch(); ExecutorService counterMutationStage = StageManager.getStage(Stage.COUNTER_MUTATION); - ExecutorService materializedViewMutationStage = StageManager.getStage(Stage.MATERIALIZED_VIEW_MUTATION); + ExecutorService viewMutationStage = StageManager.getStage(Stage.VIEW_MUTATION); ExecutorService mutationStage = StageManager.getStage(Stage.MUTATION); if (mutationStage.isTerminated() && counterMutationStage.isTerminated() - && materializedViewMutationStage.isTerminated()) + && viewMutationStage.isTerminated()) { logger.warn("Cannot drain node (did it already happen?)"); return; @@ -3894,10 +3894,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE MessagingService.instance().shutdown(); setMode(Mode.DRAINING, "clearing mutation stage", false); - materializedViewMutationStage.shutdown(); + viewMutationStage.shutdown(); counterMutationStage.shutdown(); mutationStage.shutdown(); - materializedViewMutationStage.awaitTermination(3600, TimeUnit.SECONDS); + viewMutationStage.awaitTermination(3600, TimeUnit.SECONDS); counterMutationStage.awaitTermination(3600, TimeUnit.SECONDS); mutationStage.awaitTermination(3600, TimeUnit.SECONDS); diff --git a/src/java/org/apache/cassandra/streaming/StreamReceiveTask.java b/src/java/org/apache/cassandra/streaming/StreamReceiveTask.java index cb99654b79..f261954705 100644 --- a/src/java/org/apache/cassandra/streaming/StreamReceiveTask.java +++ b/src/java/org/apache/cassandra/streaming/StreamReceiveTask.java @@ -21,6 +21,7 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,10 +34,10 @@ import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.db.lifecycle.LifecycleTransaction; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.db.rows.UnfilteredRowIterator; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.io.sstable.ISSTableScanner; import org.apache.cassandra.io.sstable.SSTableMultiWriter; import org.apache.cassandra.io.sstable.format.SSTableReader; -import org.apache.cassandra.io.sstable.format.SSTableWriter; import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.Pair; import org.apache.cassandra.utils.concurrent.Refs; @@ -127,7 +128,7 @@ public class StreamReceiveTask extends StreamTask return; } ColumnFamilyStore cfs = Keyspace.open(kscf.left).getColumnFamilyStore(kscf.right); - boolean hasMaterializedViews = cfs.materializedViewManager.allViews().iterator().hasNext(); + boolean hasViews = !Iterables.isEmpty(View.findAll(kscf.left, kscf.right)); try { @@ -143,11 +144,11 @@ public class StreamReceiveTask extends StreamTask try (Refs refs = Refs.ref(readers)) { - //We have a special path for Materialized view. - //Since the MV requires cleaning up any pre-existing state, we must put + //We have a special path for views. + //Since the view requires cleaning up any pre-existing state, we must put //all partitions through the same write path as normal mutations. - //This also ensures any 2i's are also updated - if (hasMaterializedViews) + //This also ensures any 2is are also updated + if (hasViews) { for (SSTableReader reader : readers) { @@ -183,7 +184,7 @@ public class StreamReceiveTask extends StreamTask { //We don't keep the streamed sstables since we've applied them manually //So we abort the txn and delete the streamed sstables - if (hasMaterializedViews) + if (hasViews) { cfs.forceBlockingFlush(); task.txn.abort(); diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java b/src/java/org/apache/cassandra/thrift/CassandraServer.java index 207bb6f4d5..7017bc1a16 100644 --- a/src/java/org/apache/cassandra/thrift/CassandraServer.java +++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java @@ -44,6 +44,7 @@ import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.TimeUUIDType; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.db.rows.*; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.dht.*; import org.apache.cassandra.dht.Range; import org.apache.cassandra.exceptions.*; @@ -815,7 +816,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.MODIFY); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, false); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot modify Materialized Views directly"); ThriftValidation.validateKey(metadata, key); @@ -912,7 +913,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, column_family, Permission.SELECT); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_family, false); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot modify Materialized Views directly"); ThriftValidation.validateKey(metadata, key); @@ -1108,7 +1109,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, cfName, Permission.MODIFY); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, cfName); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot modify Materialized Views directly"); ThriftValidation.validateKey(metadata, key); @@ -1325,7 +1326,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, column_path.column_family, Permission.MODIFY); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_path.column_family, isCommutativeOp); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot modify Materialized Views directly"); ThriftValidation.validateKey(metadata, key); @@ -1903,7 +1904,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, column_family, Permission.DROP); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_family); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot drop Materialized Views from Thrift"); MigrationManager.announceColumnFamilyDrop(keyspace, column_family); @@ -2015,9 +2016,9 @@ public class CassandraServer implements Cassandra.Iface if (oldCfm == null) throw new InvalidRequestException("Could not find table definition to modify."); - if (oldCfm.isMaterializedView()) + if (oldCfm.isView()) throw new InvalidRequestException("Cannot modify Materialized View table " + oldCfm.cfName + " as it may break the schema. You should use cqlsh to modify Materialized View tables instead."); - if (!oldCfm.getMaterializedViews().isEmpty()) + if (!Iterables.isEmpty(View.findAll(cf_def.keyspace, cf_def.name))) throw new InvalidRequestException("Cannot modify table with Materialized View " + oldCfm.cfName + " as it may break the schema. You should use cqlsh to modify tables with Materialized Views instead."); if (!oldCfm.isThriftCompatible()) @@ -2047,7 +2048,7 @@ public class CassandraServer implements Cassandra.Iface String keyspace = cState.getKeyspace(); cState.hasColumnFamilyAccess(keyspace, cfname, Permission.MODIFY); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, cfname, false); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot truncate Materialized Views"); if (startSessionIfRequested()) @@ -2134,7 +2135,7 @@ public class CassandraServer implements Cassandra.Iface cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.MODIFY); CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, true); - if (metadata.isMaterializedView()) + if (metadata.isView()) throw new org.apache.cassandra.exceptions.InvalidRequestException("Cannot modify Materialized Views directly"); ThriftValidation.validateKey(metadata, key); diff --git a/src/java/org/apache/cassandra/thrift/ThriftConversion.java b/src/java/org/apache/cassandra/thrift/ThriftConversion.java index 86cfe42b27..b721226f87 100644 --- a/src/java/org/apache/cassandra/thrift/ThriftConversion.java +++ b/src/java/org/apache/cassandra/thrift/ThriftConversion.java @@ -162,7 +162,7 @@ public class ThriftConversion public static KsDef toThrift(KeyspaceMetadata ksm) { List cfDefs = new ArrayList<>(); - for (CFMetaData cfm : ksm.tables) + for (CFMetaData cfm : ksm.tables) // do not include views if (cfm.isThriftCompatible()) // Don't expose CF that cannot be correctly handle by thrift; see CASSANDRA-4377 for further details cfDefs.add(toThrift(cfm)); @@ -272,8 +272,8 @@ public class ThriftConversion defaultValidator); } - // We do not allow Thrift materialized views, so we always set it to false - boolean isMaterializedView = false; + // We do not allow Thrift views, so we always set it to false + boolean isView = false; CFMetaData newCFMD = CFMetaData.create(cf_def.keyspace, cf_def.name, @@ -282,7 +282,7 @@ public class ThriftConversion isCompound, isSuper, isCounter, - isMaterializedView, + isView, defs, DatabaseDescriptor.getPartitioner()); diff --git a/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java b/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java index d6020767ec..8dff5320a1 100644 --- a/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java +++ b/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java @@ -80,6 +80,8 @@ public class NativeSSTableLoaderClient extends SSTableLoader.Client } tables.putAll(fetchTablesMetadata(keyspace, session, partitioner)); + // We only need the CFMetaData for the views, so we only load that. + tables.putAll(fetchViewMetadata(keyspace, session, partitioner)); } } @@ -111,41 +113,61 @@ public class NativeSSTableLoaderClient extends SSTableLoader.Client for (Row row : session.execute(query, keyspace)) { String name = row.getString("table_name"); - UUID id = row.getUUID("id"); - - Set flags = row.isNull("flags") - ? Collections.emptySet() - : CFMetaData.flagsFromStrings(row.getSet("flags", String.class)); - - boolean isSuper = flags.contains(CFMetaData.Flag.SUPER); - boolean isCounter = flags.contains(CFMetaData.Flag.COUNTER); - boolean isDense = flags.contains(CFMetaData.Flag.DENSE); - boolean isCompound = flags.contains(CFMetaData.Flag.COMPOUND); - boolean isMaterializedView = flags.contains(CFMetaData.Flag.VIEW); - - String columnsQuery = String.format("SELECT * FROM %s.%s WHERE keyspace_name = ? AND table_name = ?", - SchemaKeyspace.NAME, - SchemaKeyspace.COLUMNS); - - List defs = new ArrayList<>(); - for (Row colRow : session.execute(columnsQuery, keyspace, name)) - defs.add(createDefinitionFromRow(colRow, keyspace, name)); - - tables.put(name, CFMetaData.create(keyspace, - name, - id, - isDense, - isCompound, - isSuper, - isCounter, - isMaterializedView, - defs, - partitioner)); + tables.put(name, createTableMetadata(keyspace, session, partitioner, false, row, name)); } return tables; } + /* + * In the case where we are creating View CFMetaDatas, we + */ + private static Map fetchViewMetadata(String keyspace, Session session, IPartitioner partitioner) + { + Map tables = new HashMap<>(); + String query = String.format("SELECT * FROM %s.%s WHERE keyspace_name = ?", SchemaKeyspace.NAME, SchemaKeyspace.VIEWS); + + for (Row row : session.execute(query, keyspace)) + { + String name = row.getString("view_name"); + tables.put(name, createTableMetadata(keyspace, session, partitioner, true, row, name)); + } + + return tables; + } + + private static CFMetaData createTableMetadata(String keyspace, Session session, IPartitioner partitioner, boolean isView, Row row, String name) + { + UUID id = row.getUUID("id"); + Set flags = row.isNull("flags") + ? Collections.emptySet() + : CFMetaData.flagsFromStrings(row.getSet("flags", String.class)); + + boolean isSuper = flags.contains(CFMetaData.Flag.SUPER); + boolean isCounter = flags.contains(CFMetaData.Flag.COUNTER); + boolean isDense = flags.contains(CFMetaData.Flag.DENSE); + boolean isCompound = flags.contains(CFMetaData.Flag.COMPOUND); + + String columnsQuery = String.format("SELECT * FROM %s.%s WHERE keyspace_name = ? AND table_name = ?", + SchemaKeyspace.NAME, + SchemaKeyspace.COLUMNS); + + List defs = new ArrayList<>(); + for (Row colRow : session.execute(columnsQuery, keyspace, name)) + defs.add(createDefinitionFromRow(colRow, keyspace, name)); + + return CFMetaData.create(keyspace, + name, + id, + isDense, + isCompound, + isSuper, + isCounter, + isView, + defs, + partitioner); + } + private static ColumnDefinition createDefinitionFromRow(Row row, String keyspace, String table) { ColumnIdentifier name = ColumnIdentifier.getInterned(row.getBytes("column_name_bytes"), row.getString("column_name")); diff --git a/test/long/org/apache/cassandra/cql3/MaterializedViewLongTest.java b/test/long/org/apache/cassandra/cql3/ViewLongTest.java similarity index 94% rename from test/long/org/apache/cassandra/cql3/MaterializedViewLongTest.java rename to test/long/org/apache/cassandra/cql3/ViewLongTest.java index b833e60ef7..3808b739a4 100644 --- a/test/long/org/apache/cassandra/cql3/MaterializedViewLongTest.java +++ b/test/long/org/apache/cassandra/cql3/ViewLongTest.java @@ -38,10 +38,10 @@ import org.apache.cassandra.concurrent.StageManager; import org.apache.cassandra.batchlog.BatchlogManager; import org.apache.cassandra.utils.WrappedRunnable; -public class MaterializedViewLongTest extends CQLTester +public class ViewLongTest extends CQLTester { int protocolVersion = 4; - private final List materializedViews = new ArrayList<>(); + private final List views = new ArrayList<>(); @BeforeClass public static void startup() @@ -51,13 +51,13 @@ public class MaterializedViewLongTest extends CQLTester @Before public void begin() { - materializedViews.clear(); + views.clear(); } @After public void end() throws Throwable { - for (String viewName : materializedViews) + for (String viewName : views) executeNet(protocolVersion, "DROP MATERIALIZED VIEW " + viewName); } @@ -66,7 +66,7 @@ public class MaterializedViewLongTest extends CQLTester executeNet(protocolVersion, String.format(query, name)); // If exception is thrown, the view will not be added to the list; since it shouldn't have been created, this is // the desired behavior - materializedViews.add(name); + views.add(name); } @Test @@ -134,8 +134,8 @@ public class MaterializedViewLongTest extends CQLTester break; try { - // This will throw exceptions whenever there are exceptions trying to push the materialized view values - // out, caused by the view becoming overwhelmed. + // This will throw exceptions whenever there are exceptions trying to push the view values out, caused + // by the view becoming overwhelmed. BatchlogManager.instance.startBatchlogReplay().get(); } catch (Throwable ignore) diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java index 175e492858..72bfd00ece 100644 --- a/test/unit/org/apache/cassandra/SchemaLoader.java +++ b/test/unit/org/apache/cassandra/SchemaLoader.java @@ -309,7 +309,7 @@ public class SchemaLoader private static void useCompression(List schema) { for (KeyspaceMetadata ksm : schema) - for (CFMetaData cfm : ksm.tables) + for (CFMetaData cfm : ksm.tablesAndViews()) cfm.compression(CompressionParams.snappy()); } diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java index 5887c9a638..3a3b6eee06 100644 --- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java +++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java @@ -52,7 +52,7 @@ public class DatabaseDescriptorTest // test serialization of all defined test CFs. for (String keyspaceName : Schema.instance.getNonSystemKeyspaces()) { - for (CFMetaData cfm : Schema.instance.getTables(keyspaceName)) + for (CFMetaData cfm : Schema.instance.getTablesAndViews(keyspaceName)) { CFMetaData cfmDupe = ThriftConversion.fromThrift(ThriftConversion.toThrift(cfm)); assertNotNull(cfmDupe); diff --git a/test/unit/org/apache/cassandra/cql3/MaterializedViewTest.java b/test/unit/org/apache/cassandra/cql3/ViewTest.java similarity index 78% rename from test/unit/org/apache/cassandra/cql3/MaterializedViewTest.java rename to test/unit/org/apache/cassandra/cql3/ViewTest.java index f4b4a828c7..9c47707cd7 100644 --- a/test/unit/org/apache/cassandra/cql3/MaterializedViewTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java @@ -22,11 +22,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import org.junit.After; @@ -51,10 +47,10 @@ import org.apache.cassandra.serializers.TimeSerializer; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; -public class MaterializedViewTest extends CQLTester +public class ViewTest extends CQLTester { int protocolVersion = 4; - private final List materializedViews = new ArrayList<>(); + private final List views = new ArrayList<>(); @BeforeClass public static void startup() @@ -64,13 +60,13 @@ public class MaterializedViewTest extends CQLTester @Before public void begin() { - materializedViews.clear(); + views.clear(); } @After public void end() throws Throwable { - for (String viewName : materializedViews) + for (String viewName : views) executeNet(protocolVersion, "DROP MATERIALIZED VIEW " + viewName); } @@ -79,19 +75,58 @@ public class MaterializedViewTest extends CQLTester executeNet(protocolVersion, String.format(query, name)); // If exception is thrown, the view will not be added to the list; since it shouldn't have been created, this is // the desired behavior - materializedViews.add(name); + views.add(name); } - private void updateMV(String query, Object... params) throws Throwable + private void updateView(String query, Object... params) throws Throwable { executeNet(protocolVersion, query, params); - while (!(((SEPExecutor) StageManager.getStage(Stage.MATERIALIZED_VIEW_MUTATION)).getPendingTasks() == 0 - && ((SEPExecutor) StageManager.getStage(Stage.MATERIALIZED_VIEW_MUTATION)).getActiveCount() == 0)) + while (!(((SEPExecutor) StageManager.getStage(Stage.VIEW_MUTATION)).getPendingTasks() == 0 + && ((SEPExecutor) StageManager.getStage(Stage.VIEW_MUTATION)).getActiveCount() == 0)) { Thread.sleep(1); } } + @Test + public void testCaseSensitivity() throws Throwable + { + createTable("CREATE TABLE %s (\"theKey\" int, \"theClustering\" int, \"theValue\" int, PRIMARY KEY (\"theKey\", \"theClustering\"))"); + + execute("USE " + keyspace()); + executeNet(protocolVersion, "USE " + keyspace()); + + execute("INSERT INTO %s (\"theKey\", \"theClustering\", \"theValue\") VALUES (?, ?, ?)", 0, 0, 0); + + createView("mv_test", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s " + + "WHERE \"theKey\" IS NOT NULL AND \"theClustering\" IS NOT NULL AND \"theValue\" IS NOT NULL " + + "PRIMARY KEY (\"theKey\", \"theClustering\")"); + + while (!SystemKeyspace.isViewBuilt(keyspace(), "mv_test")) + Thread.sleep(10); + createView("mv_test2", "CREATE MATERIALIZED VIEW %s AS SELECT \"theKey\", \"theClustering\", \"theValue\" FROM %%s " + + "WHERE \"theKey\" IS NOT NULL AND \"theClustering\" IS NOT NULL AND \"theValue\" IS NOT NULL " + + "PRIMARY KEY (\"theKey\", \"theClustering\")"); + while (!SystemKeyspace.isViewBuilt(keyspace(), "mv_test2")) + Thread.sleep(10); + + for (String mvname : Arrays.asList("mv_test", "mv_test2")) + { + assertRows(execute("SELECT \"theKey\", \"theClustering\", \"theValue\" FROM " + mvname), + row(0, 0, 0) + ); + } + + executeNet(protocolVersion, "ALTER TABLE %s RENAME \"theClustering\" TO \"Col\""); + + for (String mvname : Arrays.asList("mv_test", "mv_test2")) + { + assertRows(execute("SELECT \"theKey\", \"Col\", \"theValue\" FROM " + mvname), + row(0, 0, 0) + ); + } + } + @Test public void testPartitionTombstone() throws Throwable { @@ -102,13 +137,13 @@ public class MaterializedViewTest extends CQLTester createView("view1", "CREATE MATERIALIZED VIEW view1 AS SELECT k1 FROM %%s WHERE k1 IS NOT NULL AND c1 IS NOT NULL AND val IS NOT NULL PRIMARY KEY (val, k1, c1)"); - updateMV("INSERT INTO %s (k1, c1, val) VALUES (1, 2, 200)"); - updateMV("INSERT INTO %s (k1, c1, val) VALUES (1, 3, 300)"); + updateView("INSERT INTO %s (k1, c1, val) VALUES (1, 2, 200)"); + updateView("INSERT INTO %s (k1, c1, val) VALUES (1, 3, 300)"); Assert.assertEquals(1, execute("select * from %s").size()); Assert.assertEquals(1, execute("select * from view1").size()); - updateMV("DELETE FROM %s WHERE k1 = 1"); + updateView("DELETE FROM %s WHERE k1 = 1"); Assert.assertEquals(0, execute("select * from %s").size()); Assert.assertEquals(0, execute("select * from view1").size()); @@ -124,13 +159,13 @@ public class MaterializedViewTest extends CQLTester createView("view1", "CREATE MATERIALIZED VIEW view1 AS SELECT k1 FROM %%s WHERE k1 IS NOT NULL AND c1 IS NOT NULL AND val IS NOT NULL PRIMARY KEY (val, k1, c1)"); - updateMV("INSERT INTO %s (k1, c1, val) VALUES (1, 2, 200)"); - updateMV("INSERT INTO %s (k1, c1, val) VALUES (1, 3, 300)"); + updateView("INSERT INTO %s (k1, c1, val) VALUES (1, 2, 200)"); + updateView("INSERT INTO %s (k1, c1, val) VALUES (1, 3, 300)"); Assert.assertEquals(2, execute("select * from %s").size()); Assert.assertEquals(2, execute("select * from view1").size()); - updateMV("DELETE FROM %s WHERE k1 = 1 and c1 = 3"); + updateView("DELETE FROM %s WHERE k1 = 1 and c1 = 3"); Assert.assertEquals(1, execute("select * from %s").size()); Assert.assertEquals(1, execute("select * from view1").size()); @@ -198,11 +233,11 @@ public class MaterializedViewTest extends CQLTester executeNet(protocolVersion, "USE " + keyspace()); createView("mv1_test", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE bigintval IS NOT NULL AND k IS NOT NULL AND asciival IS NOT NULL PRIMARY KEY (bigintval, k, asciival)"); - updateMV("INSERT INTO %s(k,asciival,bigintval)VALUES(?,?,?)", 0, "foo", 1L); + updateView("INSERT INTO %s(k,asciival,bigintval)VALUES(?,?,?)", 0, "foo", 1L); try { - updateMV("INSERT INTO mv1_test(k,asciival,bigintval) VALUES(?,?,?)", 1, "foo", 2L); + updateView("INSERT INTO mv1_test(k,asciival,bigintval) VALUES(?,?,?)", 1, "foo", 2L); Assert.fail("Shouldn't be able to modify a MV directly"); } catch (Exception e) @@ -234,7 +269,7 @@ public class MaterializedViewTest extends CQLTester CFMetaData metadata = Schema.instance.getCFMetaData(keyspace(), "mv1_test"); Assert.assertNotNull(metadata.getColumnDefinition(ByteBufferUtil.bytes("foo"))); - updateMV("INSERT INTO %s(k,asciival,bigintval,foo)VALUES(?,?,?,?)", 0, "foo", 1L, "bar"); + updateView("INSERT INTO %s(k,asciival,bigintval,foo)VALUES(?,?,?,?)", 0, "foo", 1L, "bar"); assertRows(execute("SELECT foo from %s"), row("bar")); //Test alter rename @@ -271,7 +306,7 @@ public class MaterializedViewTest extends CQLTester createView("mv_static", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)"); for (int i = 0; i < 100; i++) - updateMV("INSERT into %s (k,c,sval,val)VALUES(?,?,?,?)", 0, i % 2, "bar" + i, "baz"); + updateView("INSERT into %s (k,c,sval,val)VALUES(?,?,?,?)", 0, i % 2, "bar" + i, "baz"); Assert.assertEquals(2, execute("select * from %s").size()); @@ -298,7 +333,7 @@ public class MaterializedViewTest extends CQLTester createView("mv_tstest", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)"); for (int i = 0; i < 100; i++) - updateMV("INSERT into %s (k,c,val)VALUES(?,?,?)", 0, i % 2, "baz"); + updateView("INSERT into %s (k,c,val)VALUES(?,?,?)", 0, i % 2, "baz"); Keyspace.open(keyspace()).getColumnFamilyStore(currentTable()).forceBlockingFlush(); @@ -309,13 +344,13 @@ public class MaterializedViewTest extends CQLTester assertRows(execute("SELECT c from mv_tstest where k = 0 and val = ?", "baz"), row(0), row(1)); //Make sure an old TS does nothing - updateMV("UPDATE %s USING TIMESTAMP 100 SET val = ? where k = ? AND c = ?", "bar", 0, 0); + updateView("UPDATE %s USING TIMESTAMP 100 SET val = ? where k = ? AND c = ?", "bar", 0, 0); assertRows(execute("SELECT val from %s where k = 0 and c = 0"), row("baz")); assertRows(execute("SELECT c from mv_tstest where k = 0 and val = ?", "baz"), row(0), row(1)); assertRows(execute("SELECT c from mv_tstest where k = 0 and val = ?", "bar")); //Latest TS - updateMV("UPDATE %s SET val = ? where k = ? AND c = ?", "bar", 0, 0); + updateView("UPDATE %s SET val = ? where k = ? AND c = ?", "bar", 0, 0); assertRows(execute("SELECT val from %s where k = 0 and c = 0"), row("bar")); assertRows(execute("SELECT c from mv_tstest where k = 0 and val = ?", "bar"), row(0)); assertRows(execute("SELECT c from mv_tstest where k = 0 and val = ?", "baz"), row(1)); @@ -503,7 +538,7 @@ public class MaterializedViewTest extends CQLTester createView("mv_test1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE textval2 IS NOT NULL AND k IS NOT NULL AND asciival IS NOT NULL AND bigintval IS NOT NULL AND textval1 IS NOT NULL PRIMARY KEY ((textval2, k), asciival, bigintval, textval1)"); for (int i = 0; i < 100; i++) - updateMV("INSERT into %s (k,asciival,bigintval,textval1,textval2)VALUES(?,?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i, "baz"); + updateView("INSERT into %s (k,asciival,bigintval,textval1,textval2)VALUES(?,?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i, "baz"); Assert.assertEquals(50, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 0").size()); Assert.assertEquals(50, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 1").size()); @@ -527,7 +562,7 @@ public class MaterializedViewTest extends CQLTester Assert.assertEquals(100, execute("select asciival from mv_test3 where textval2 = ? and k = ?", "baz", 0).size()); //Write a RT and verify the data is removed from index - updateMV("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval = ?", 0, "foo", 0L); + updateView("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval = ?", 0, "foo", 0L); Assert.assertEquals(50, execute("select asciival from mv_test3 where textval2 = ? and k = ?", "baz", 0).size()); } @@ -550,7 +585,7 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE textval1 IS NOT NULL AND k IS NOT NULL AND asciival IS NOT NULL AND bigintval IS NOT NULL PRIMARY KEY ((textval1, k), asciival, bigintval)"); for (int i = 0; i < 100; i++) - updateMV("INSERT into %s (k,asciival,bigintval,textval1)VALUES(?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i); + updateView("INSERT into %s (k,asciival,bigintval,textval1)VALUES(?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i); Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 0").size()); Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 1").size()); @@ -560,7 +595,7 @@ public class MaterializedViewTest extends CQLTester Assert.assertEquals(2, execute("select * from mv").size()); //Write a RT and verify the data is removed from index - updateMV("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval = ?", 0, "foo", 0L); + updateView("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval = ?", 0, "foo", 0L); Assert.assertEquals(1, execute("select * from %s").size()); Assert.assertEquals(1, execute("select * from mv").size()); @@ -583,7 +618,7 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE textval1 IS NOT NULL AND k IS NOT NULL AND asciival IS NOT NULL AND bigintval IS NOT NULL PRIMARY KEY ((textval1, k), asciival, bigintval)"); for (int i = 0; i < 100; i++) - updateMV("INSERT into %s (k,asciival,bigintval,textval1)VALUES(?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i); + updateView("INSERT into %s (k,asciival,bigintval,textval1)VALUES(?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i); Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 0").size()); Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 1").size()); @@ -593,7 +628,7 @@ public class MaterializedViewTest extends CQLTester Assert.assertEquals(2, execute("select * from mv").size()); //Write a RT and verify the data is removed from index - updateMV("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval >= ?", 0, "foo", 0L); + updateView("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval >= ?", 0, "foo", 0L); Assert.assertEquals(0, execute("select * from %s").size()); Assert.assertEquals(0, execute("select * from mv").size()); @@ -688,8 +723,8 @@ public class MaterializedViewTest extends CQLTester } } - updateMV("INSERT INTO %s (k, asciival, bigintval) VALUES (?, ?, fromJson(?))", 0, "ascii text", "123123123123"); - updateMV("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); + updateView("INSERT INTO %s (k, asciival, bigintval) VALUES (?, ?, fromJson(?))", 0, "ascii text", "123123123123"); + updateView("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); assertRows(execute("SELECT bigintval FROM %s WHERE k = ? and asciival = ?", 0, "ascii text"), row(123123123123L)); //Check the MV @@ -700,7 +735,7 @@ public class MaterializedViewTest extends CQLTester //UPDATE BASE - updateMV("INSERT INTO %s (k, asciival, bigintval) VALUES (?, ?, fromJson(?))", 0, "ascii text", "1"); + updateView("INSERT INTO %s (k, asciival, bigintval) VALUES (?, ?, fromJson(?))", 0, "ascii text", "1"); assertRows(execute("SELECT bigintval FROM %s WHERE k = ? and asciival = ?", 0, "ascii text"), row(1L)); //Check the MV @@ -712,7 +747,7 @@ public class MaterializedViewTest extends CQLTester //test truncate also truncates all MV - updateMV("TRUNCATE %s"); + updateView("TRUNCATE %s"); assertRows(execute("SELECT bigintval FROM %s WHERE k = ? and asciival = ?", 0, "ascii text")); assertRows(execute("SELECT k, bigintval from mv1_asciival WHERE asciival = ?", "ascii text")); @@ -734,12 +769,12 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)"); - updateMV("INSERT INTO %s (k, intval, listval) VALUES (?, ?, fromJson(?))", 0, 0, "[1, 2, 3]"); + updateView("INSERT INTO %s (k, intval, listval) VALUES (?, ?, fromJson(?))", 0, 0, "[1, 2, 3]"); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3))); assertRows(execute("SELECT k, listval from mv WHERE intval = ?", 0), row(0, list(1, 2, 3))); - updateMV("INSERT INTO %s (k, intval) VALUES (?, ?)", 1, 1); - updateMV("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 1, "[1, 2, 3]"); + updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 1, 1); + updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 1, "[1, 2, 3]"); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 1), row(1, list(1, 2, 3))); assertRows(execute("SELECT k, listval from mv WHERE intval = ?", 1), row(1, list(1, 2, 3))); } @@ -757,11 +792,11 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)"); - updateMV("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 0); + updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 0); assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 0)); assertRows(execute("SELECT k, intval from mv WHERE intval = ?", 0), row(0, 0)); - updateMV("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 1); + updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 1); assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 1)); assertRows(execute("SELECT k, intval from mv WHERE intval = ?", 1), row(0, 1)); } @@ -780,18 +815,45 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE k IS NOT NULL AND decimalval IS NOT NULL PRIMARY KEY (decimalval, k)"); - updateMV("INSERT INTO %s (k, asciival) VALUES (?, ?)", 0, "ascii text"); - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123"); + updateView("INSERT INTO %s (k, asciival) VALUES (?, ?)", 0, "ascii text"); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123"); assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123"))); assertRows(execute("SELECT k, asciival from mv WHERE decimalval = fromJson(?)", "123123.123123")); assertRows(execute("SELECT k, asciival from mv WHERE decimalval = fromJson(?)", "123123"), row(0, "ascii text")); // accept strings for numbers that cannot be represented as doubles - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"123123.123123\""); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"123123.123123\""); assertRows(execute("SELECT k, decimalval, asciival FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123.123123"), "ascii text")); assertRows(execute("SELECT k, asciival from mv WHERE decimalval = fromJson(?)", "\"123123.123123\""), row(0, "ascii text")); } + @Test + public void testReuseName() throws Throwable + { + createTable("CREATE TABLE %s (" + + "k int, " + + "intval int, " + + "PRIMARY KEY (k))"); + + execute("USE " + keyspace()); + executeNet(protocolVersion, "USE " + keyspace()); + + createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)"); + + updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 0); + assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 0)); + assertRows(execute("SELECT k, intval from mv WHERE intval = ?", 0), row(0, 0)); + + executeNet(protocolVersion, "DROP MATERIALIZED VIEW mv"); + views.remove("mv"); + + createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)"); + + updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 1); + assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 1)); + assertRows(execute("SELECT k, intval from mv WHERE intval = ?", 1), row(0, 1)); + } + @Test public void testAllTypes() throws Throwable { @@ -856,10 +918,10 @@ public class MaterializedViewTest extends CQLTester createFunctionOverload(func1, "int", "CREATE FUNCTION %s (a text) CALLED ON NULL INPUT RETURNS text LANGUAGE java AS $$ return new String(a); $$"); // ================ ascii ================ - updateMV("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); + updateView("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); assertRows(execute("SELECT k, asciival FROM %s WHERE k = ?", 0), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii \\\" text\""); + updateView("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii \\\" text\""); assertRows(execute("SELECT k, asciival FROM %s WHERE k = ?", 0), row(0, "ascii \" text")); // test that we can use fromJson() in other valid places in queries @@ -869,192 +931,192 @@ public class MaterializedViewTest extends CQLTester assertRows(execute("SELECT k, udtval from mv_asciival WHERE asciival = ?", "ascii text")); assertRows(execute("SELECT k, udtval from mv_asciival WHERE asciival = ?", "ascii \" text"), row(0, null)); - updateMV("UPDATE %s SET asciival = fromJson(?) WHERE k = fromJson(?)", "\"ascii \\\" text\"", "0"); + updateView("UPDATE %s SET asciival = fromJson(?) WHERE k = fromJson(?)", "\"ascii \\\" text\"", "0"); assertRows(execute("SELECT k, udtval from mv_asciival WHERE asciival = ?", "ascii \" text"), row(0, null)); - updateMV("DELETE FROM %s WHERE k = fromJson(?)", "0"); + updateView("DELETE FROM %s WHERE k = fromJson(?)", "0"); assertRows(execute("SELECT k, asciival FROM %s WHERE k = ?", 0)); assertRows(execute("SELECT k, udtval from mv_asciival WHERE asciival = ?", "ascii \" text")); - updateMV("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); + updateView("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); assertRows(execute("SELECT k, udtval from mv_asciival WHERE asciival = ?", "ascii text"), row(0, null)); // ================ bigint ================ - updateMV("INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "123123123123"); + updateView("INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "123123123123"); assertRows(execute("SELECT k, bigintval FROM %s WHERE k = ?", 0), row(0, 123123123123L)); assertRows(execute("SELECT k, asciival from mv_bigintval WHERE bigintval = ?", 123123123123L), row(0, "ascii text")); // ================ blob ================ - updateMV("INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"0x00000001\""); + updateView("INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"0x00000001\""); assertRows(execute("SELECT k, blobval FROM %s WHERE k = ?", 0), row(0, ByteBufferUtil.bytes(1))); assertRows(execute("SELECT k, asciival from mv_blobval WHERE blobval = ?", ByteBufferUtil.bytes(1)), row(0, "ascii text")); // ================ boolean ================ - updateMV("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "true"); + updateView("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "true"); assertRows(execute("SELECT k, booleanval FROM %s WHERE k = ?", 0), row(0, true)); assertRows(execute("SELECT k, asciival from mv_booleanval WHERE booleanval = ?", true), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "false"); + updateView("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "false"); assertRows(execute("SELECT k, booleanval FROM %s WHERE k = ?", 0), row(0, false)); assertRows(execute("SELECT k, asciival from mv_booleanval WHERE booleanval = ?", true)); assertRows(execute("SELECT k, asciival from mv_booleanval WHERE booleanval = ?", false), row(0, "ascii text")); // ================ date ================ - updateMV("INSERT INTO %s (k, dateval) VALUES (?, fromJson(?))", 0, "\"1987-03-23\""); + updateView("INSERT INTO %s (k, dateval) VALUES (?, fromJson(?))", 0, "\"1987-03-23\""); assertRows(execute("SELECT k, dateval FROM %s WHERE k = ?", 0), row(0, SimpleDateSerializer.dateStringToDays("1987-03-23"))); assertRows(execute("SELECT k, asciival from mv_dateval WHERE dateval = fromJson(?)", "\"1987-03-23\""), row(0, "ascii text")); // ================ decimal ================ - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123.123123"); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123.123123"); assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123.123123"))); assertRows(execute("SELECT k, asciival from mv_decimalval WHERE decimalval = fromJson(?)", "123123.123123"), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123"); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123"); assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123"))); assertRows(execute("SELECT k, asciival from mv_decimalval WHERE decimalval = fromJson(?)", "123123.123123")); assertRows(execute("SELECT k, asciival from mv_decimalval WHERE decimalval = fromJson(?)", "123123"), row(0, "ascii text")); // accept strings for numbers that cannot be represented as doubles - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"123123.123123\""); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"123123.123123\""); assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123.123123"))); - updateMV("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"-1.23E-12\""); + updateView("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"-1.23E-12\""); assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("-1.23E-12"))); assertRows(execute("SELECT k, asciival from mv_decimalval WHERE decimalval = fromJson(?)", "\"-1.23E-12\""), row(0, "ascii text")); // ================ double ================ - updateMV("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123.123123"); + updateView("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123.123123"); assertRows(execute("SELECT k, doubleval FROM %s WHERE k = ?", 0), row(0, 123123.123123d)); assertRows(execute("SELECT k, asciival from mv_doubleval WHERE doubleval = fromJson(?)", "123123.123123"), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123"); + updateView("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123"); assertRows(execute("SELECT k, doubleval FROM %s WHERE k = ?", 0), row(0, 123123.0d)); assertRows(execute("SELECT k, asciival from mv_doubleval WHERE doubleval = fromJson(?)", "123123"), row(0, "ascii text")); // ================ float ================ - updateMV("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123.123123"); + updateView("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123.123123"); assertRows(execute("SELECT k, floatval FROM %s WHERE k = ?", 0), row(0, 123123.123123f)); assertRows(execute("SELECT k, asciival from mv_floatval WHERE floatval = fromJson(?)", "123123.123123"), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123"); + updateView("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123"); assertRows(execute("SELECT k, floatval FROM %s WHERE k = ?", 0), row(0, 123123.0f)); assertRows(execute("SELECT k, asciival from mv_floatval WHERE floatval = fromJson(?)", "123123"), row(0, "ascii text")); // ================ inet ================ - updateMV("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"127.0.0.1\""); + updateView("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"127.0.0.1\""); assertRows(execute("SELECT k, inetval FROM %s WHERE k = ?", 0), row(0, InetAddress.getByName("127.0.0.1"))); assertRows(execute("SELECT k, asciival from mv_inetval WHERE inetval = fromJson(?)", "\"127.0.0.1\""), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"::1\""); + updateView("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"::1\""); assertRows(execute("SELECT k, inetval FROM %s WHERE k = ?", 0), row(0, InetAddress.getByName("::1"))); assertRows(execute("SELECT k, asciival from mv_inetval WHERE inetval = fromJson(?)", "\"127.0.0.1\"")); assertRows(execute("SELECT k, asciival from mv_inetval WHERE inetval = fromJson(?)", "\"::1\""), row(0, "ascii text")); // ================ int ================ - updateMV("INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "123123"); + updateView("INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "123123"); assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 123123)); assertRows(execute("SELECT k, asciival from mv_intval WHERE intval = fromJson(?)", "123123"), row(0, "ascii text")); // ================ text (varchar) ================ - updateMV("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"some \\\" text\""); + updateView("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"some \\\" text\""); assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "some \" text")); - updateMV("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"\\u2013\""); + updateView("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"\\u2013\""); assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "\u2013")); assertRows(execute("SELECT k, asciival from mv_textval WHERE textval = fromJson(?)", "\"\\u2013\""), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"abcd\""); + updateView("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"abcd\""); assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "abcd")); assertRows(execute("SELECT k, asciival from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, "ascii text")); // ================ time ================ - updateMV("INSERT INTO %s (k, timeval) VALUES (?, fromJson(?))", 0, "\"07:35:07.000111222\""); + updateView("INSERT INTO %s (k, timeval) VALUES (?, fromJson(?))", 0, "\"07:35:07.000111222\""); assertRows(execute("SELECT k, timeval FROM %s WHERE k = ?", 0), row(0, TimeSerializer.timeStringToLong("07:35:07.000111222"))); assertRows(execute("SELECT k, asciival from mv_timeval WHERE timeval = fromJson(?)", "\"07:35:07.000111222\""), row(0, "ascii text")); // ================ timestamp ================ - updateMV("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "123123123123"); + updateView("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "123123123123"); assertRows(execute("SELECT k, timestampval FROM %s WHERE k = ?", 0), row(0, new Date(123123123123L))); assertRows(execute("SELECT k, asciival from mv_timestampval WHERE timestampval = fromJson(?)", "123123123123"), row(0, "ascii text")); - updateMV("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "\"2014-01-01\""); + updateView("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "\"2014-01-01\""); assertRows(execute("SELECT k, timestampval FROM %s WHERE k = ?", 0), row(0, new SimpleDateFormat("y-M-d").parse("2014-01-01"))); assertRows(execute("SELECT k, asciival from mv_timestampval WHERE timestampval = fromJson(?)", "\"2014-01-01\""), row(0, "ascii text")); // ================ timeuuid ================ - updateMV("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); + updateView("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); assertRows(execute("SELECT k, timeuuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); - updateMV("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); + updateView("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); assertRows(execute("SELECT k, timeuuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); assertRows(execute("SELECT k, asciival from mv_timeuuidval WHERE timeuuidval = fromJson(?)", "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""), row(0, "ascii text")); // ================ uuidval ================ - updateMV("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); + updateView("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); assertRows(execute("SELECT k, uuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); - updateMV("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); + updateView("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); assertRows(execute("SELECT k, uuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); assertRows(execute("SELECT k, asciival from mv_uuidval WHERE uuidval = fromJson(?)", "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""), row(0, "ascii text")); // ================ varint ================ - updateMV("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "123123123123"); + updateView("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "123123123123"); assertRows(execute("SELECT k, varintval FROM %s WHERE k = ?", 0), row(0, new BigInteger("123123123123"))); assertRows(execute("SELECT k, asciival from mv_varintval WHERE varintval = fromJson(?)", "123123123123"), row(0, "ascii text")); // accept strings for numbers that cannot be represented as longs - updateMV("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "\"1234567890123456789012345678901234567890\""); + updateView("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "\"1234567890123456789012345678901234567890\""); assertRows(execute("SELECT k, varintval FROM %s WHERE k = ?", 0), row(0, new BigInteger("1234567890123456789012345678901234567890"))); assertRows(execute("SELECT k, asciival from mv_varintval WHERE varintval = fromJson(?)", "\"1234567890123456789012345678901234567890\""), row(0, "ascii text")); // ================ lists ================ - updateMV("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); + updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(1, 2, 3))); - updateMV("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[1]"); + updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[1]"); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(1))); - updateMV("UPDATE %s SET listval = listval + fromJson(?) WHERE k = ?", "[2]", 0); + updateView("UPDATE %s SET listval = listval + fromJson(?) WHERE k = ?", "[2]", 0); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1, 2))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(1, 2))); - updateMV("UPDATE %s SET listval = fromJson(?) + listval WHERE k = ?", "[0]", 0); + updateView("UPDATE %s SET listval = fromJson(?) + listval WHERE k = ?", "[0]", 0); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(0, 1, 2))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(0, 1, 2))); - updateMV("UPDATE %s SET listval[1] = fromJson(?) WHERE k = ?", "10", 0); + updateView("UPDATE %s SET listval[1] = fromJson(?) WHERE k = ?", "10", 0); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(0, 10, 2))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(0, 10, 2))); - updateMV("DELETE listval[1] FROM %s WHERE k = ?", 0); + updateView("DELETE listval[1] FROM %s WHERE k = ?", 0); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(0, 2))); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(0, 2))); - updateMV("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[]"); + updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[]"); assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, null)); assertRows(execute("SELECT k, listval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, null)); // frozen - updateMV("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); + updateView("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); assertRows(execute("SELECT k, frozenlistval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3))); assertRows(execute("SELECT k, frozenlistval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(1, 2, 3))); assertRows(execute("SELECT k, textval from mv_frozenlistval where frozenlistval = fromJson(?)", "[1, 2, 3]"), row(0, "abcd")); - updateMV("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[3, 2, 1]"); + updateView("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[3, 2, 1]"); assertRows(execute("SELECT k, frozenlistval FROM %s WHERE k = ?", 0), row(0, list(3, 2, 1))); assertRows(execute("SELECT k, textval from mv_frozenlistval where frozenlistval = fromJson(?)", "[1, 2, 3]")); assertRows(execute("SELECT k, textval from mv_frozenlistval where frozenlistval = fromJson(?)", "[3, 2, 1]"), row(0, "abcd")); assertRows(execute("SELECT k, frozenlistval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list(3, 2, 1))); - updateMV("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[]"); + updateView("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[]"); assertRows(execute("SELECT k, frozenlistval FROM %s WHERE k = ?", 0), row(0, list())); assertRows(execute("SELECT k, frozenlistval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, list())); // ================ sets ================ - updateMV("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", - 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + updateView("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) ); @@ -1062,45 +1124,45 @@ public class MaterializedViewTest extends CQLTester row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))))); // duplicates are okay, just like in CQL - updateMV("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", - 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + updateView("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) ); assertRows(execute("SELECT k, setval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))))); - updateMV("UPDATE %s SET setval = setval + fromJson(?) WHERE k = ?", "[\"6bddc89a-5644-0000-97fc-56847afe9799\"]", 0); + updateView("UPDATE %s SET setval = setval + fromJson(?) WHERE k = ?", "[\"6bddc89a-5644-0000-97fc-56847afe9799\"]", 0); assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-5644-0000-97fc-56847afe9799"), UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) ); assertRows(execute("SELECT k, setval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, set(UUID.fromString("6bddc89a-5644-0000-97fc-56847afe9799"), UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))))); - updateMV("UPDATE %s SET setval = setval - fromJson(?) WHERE k = ?", "[\"6bddc89a-5644-0000-97fc-56847afe9799\"]", 0); + updateView("UPDATE %s SET setval = setval - fromJson(?) WHERE k = ?", "[\"6bddc89a-5644-0000-97fc-56847afe9799\"]", 0); assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) ); assertRows(execute("SELECT k, setval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))))); - updateMV("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "[]"); + updateView("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "[]"); assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, null)); assertRows(execute("SELECT k, setval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, null)); // frozen - updateMV("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", - 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + updateView("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); assertRows(execute("SELECT k, frozensetval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) ); assertRows(execute("SELECT k, frozensetval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))))); - updateMV("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", - 0, "[\"6bddc89a-0000-11e4-97fc-56847afe9799\", \"6bddc89a-5644-11e4-97fc-56847afe9798\"]"); + updateView("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-0000-11e4-97fc-56847afe9799\", \"6bddc89a-5644-11e4-97fc-56847afe9798\"]"); assertRows(execute("SELECT k, frozensetval FROM %s WHERE k = ?", 0), row(0, set(UUID.fromString("6bddc89a-0000-11e4-97fc-56847afe9799"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798")))) ); @@ -1108,54 +1170,54 @@ public class MaterializedViewTest extends CQLTester row(0, set(UUID.fromString("6bddc89a-0000-11e4-97fc-56847afe9799"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"))))); // ================ maps ================ - updateMV("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); + updateView("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2))); assertRows(execute("SELECT k, mapval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, map("a", 1, "b", 2))); - updateMV("UPDATE %s SET mapval[?] = ? WHERE k = ?", "c", 3, 0); + updateView("UPDATE %s SET mapval[?] = ? WHERE k = ?", "c", 3, 0); assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2, "c", 3)) ); assertRows(execute("SELECT k, mapval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, map("a", 1, "b", 2, "c", 3))); - updateMV("UPDATE %s SET mapval[?] = ? WHERE k = ?", "b", 10, 0); + updateView("UPDATE %s SET mapval[?] = ? WHERE k = ?", "b", 10, 0); assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 10, "c", 3)) ); assertRows(execute("SELECT k, mapval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, map("a", 1, "b", 10, "c", 3))); - updateMV("DELETE mapval[?] FROM %s WHERE k = ?", "b", 0); + updateView("DELETE mapval[?] FROM %s WHERE k = ?", "b", 0); assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "c", 3)) ); assertRows(execute("SELECT k, mapval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, map("a", 1, "c", 3))); - updateMV("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{}"); + updateView("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{}"); assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, null)); assertRows(execute("SELECT k, mapval from mv_textval WHERE textval = fromJson(?)", "\"abcd\""), row(0, null)); // frozen - updateMV("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); + updateView("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); assertRows(execute("SELECT k, frozenmapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2))); assertRows(execute("SELECT k, textval FROM mv_frozenmapval WHERE frozenmapval = fromJson(?)", "{\"a\": 1, \"b\": 2}"), row(0, "abcd")); - updateMV("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"b\": 2, \"a\": 3}"); + updateView("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"b\": 2, \"a\": 3}"); assertRows(execute("SELECT k, frozenmapval FROM %s WHERE k = ?", 0), row(0, map("a", 3, "b", 2))); assertRows(execute("SELECT k, frozenmapval FROM %s WHERE k = ?", 0), row(0, map("a", 3, "b", 2))); // ================ tuples ================ - updateMV("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + updateView("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); assertRows(execute("SELECT k, tupleval FROM %s WHERE k = ?", 0), row(0, tuple(1, "foobar", UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))) ); assertRows(execute("SELECT k, textval FROM mv_tupleval WHERE tupleval = ?", tuple(1, "foobar", UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))), row(0, "abcd")); - updateMV("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, null, \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + updateView("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, null, \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); assertRows(execute("SELECT k, tupleval FROM %s WHERE k = ?", 0), row(0, tuple(1, null, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))) ); @@ -1164,7 +1226,7 @@ public class MaterializedViewTest extends CQLTester row(0, "abcd")); // ================ UDTs ================ - updateMV("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); + updateView("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) ); @@ -1172,7 +1234,7 @@ public class MaterializedViewTest extends CQLTester row(0, "abcd")); // order of fields shouldn't matter - updateMV("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"a\": 1, \"c\": [\"foo\", \"bar\"]}"); + updateView("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"a\": 1, \"c\": [\"foo\", \"bar\"]}"); assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) ); @@ -1180,7 +1242,7 @@ public class MaterializedViewTest extends CQLTester row(0, "abcd")); // test nulls - updateMV("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": null, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); + updateView("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": null, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), row(0, null, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) ); @@ -1189,7 +1251,7 @@ public class MaterializedViewTest extends CQLTester row(0, "abcd")); // test missing fields - updateMV("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\"}"); + updateView("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\"}"); assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), null) ); @@ -1213,10 +1275,10 @@ public class MaterializedViewTest extends CQLTester createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE c IS NOT NULL AND a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (c, a, b)"); - updateMV("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TTL 5", 1, 1, 1, 1); + updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TTL 5", 1, 1, 1, 1); Thread.sleep(TimeUnit.SECONDS.toMillis(3)); - updateMV("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 1, 1, 2); + updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 1, 1, 2); Thread.sleep(TimeUnit.SECONDS.toMillis(3)); List results = executeNet(protocolVersion, "SELECT d FROM mv WHERE c = 2 AND a = 1 AND b = 1").all(); @@ -1239,7 +1301,7 @@ public class MaterializedViewTest extends CQLTester for (int i = 0; i < 50; i++) { - updateMV("INSERT INTO %s (a, b, c) VALUES (?, ?, ?) USING TIMESTAMP 1", 1, 1, i); + updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?) USING TIMESTAMP 1", 1, 1, i); } ResultSet mvRows = executeNet(protocolVersion, "SELECT c FROM mv"); @@ -1248,4 +1310,46 @@ public class MaterializedViewTest extends CQLTester int expected = rows.get(0).getInt("c"); assertRowsNet(protocolVersion, mvRows, row(expected)); } + + @Test + public void testClusteringOrder() throws Throwable + { + createTable("CREATE TABLE %s (" + + "a int," + + "b int," + + "c int," + + "d int," + + "PRIMARY KEY (a, b, c))" + + "WITH CLUSTERING ORDER BY (b ASC, c DESC)"); + + executeNet(protocolVersion, "USE " + keyspace()); + + createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE b IS NOT NULL AND c IS NOT NULL PRIMARY KEY (a, b, c) WITH CLUSTERING ORDER BY (b DESC)"); + createView("mv2", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE b IS NOT NULL AND c IS NOT NULL PRIMARY KEY (a, c, b) WITH CLUSTERING ORDER BY (c ASC)"); + createView("mv3", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE b IS NOT NULL AND c IS NOT NULL PRIMARY KEY (a, b, c)"); + createView("mv4", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE b IS NOT NULL AND c IS NOT NULL PRIMARY KEY (a, c, b) WITH CLUSTERING ORDER BY (c DESC)"); + + updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TIMESTAMP 1", 1, 1, 1, 1); + updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TIMESTAMP 1", 1, 2, 2, 2); + + ResultSet mvRows = executeNet(protocolVersion, "SELECT b FROM mv1"); + assertRowsNet(protocolVersion, mvRows, + row(2), + row(1)); + + mvRows = executeNet(protocolVersion, "SELECT c FROM mv2"); + assertRowsNet(protocolVersion, mvRows, + row(1), + row(2)); + + mvRows = executeNet(protocolVersion, "SELECT b FROM mv3"); + assertRowsNet(protocolVersion, mvRows, + row(1), + row(2)); + + mvRows = executeNet(protocolVersion, "SELECT c FROM mv4"); + assertRowsNet(protocolVersion, mvRows, + row(2), + row(1)); + } } diff --git a/test/unit/org/apache/cassandra/db/view/MaterializedViewUtilsTest.java b/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java similarity index 91% rename from test/unit/org/apache/cassandra/db/view/MaterializedViewUtilsTest.java rename to test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java index 25447146cc..8fd0cfb4f9 100644 --- a/test/unit/org/apache/cassandra/db/view/MaterializedViewUtilsTest.java +++ b/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java @@ -40,7 +40,7 @@ import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.schema.ReplicationParams; import org.apache.cassandra.service.StorageService; -public class MaterializedViewUtilsTest +public class ViewUtilsTest { @BeforeClass public static void setUp() throws ConfigurationException @@ -74,9 +74,9 @@ public class MaterializedViewUtilsTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, replicationMap)); Schema.instance.setKeyspaceMetadata(meta); - InetAddress naturalEndpoint = MaterializedViewUtils.getViewNaturalEndpoint("Keyspace1", - new StringToken("CA"), - new StringToken("BB")); + InetAddress naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", + new StringToken("CA"), + new StringToken("BB")); Assert.assertEquals(InetAddress.getByName("127.0.0.2"), naturalEndpoint); } @@ -106,9 +106,9 @@ public class MaterializedViewUtilsTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, replicationMap)); Schema.instance.setKeyspaceMetadata(meta); - InetAddress naturalEndpoint = MaterializedViewUtils.getViewNaturalEndpoint("Keyspace1", - new StringToken("CA"), - new StringToken("BB")); + InetAddress naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", + new StringToken("CA"), + new StringToken("BB")); Assert.assertEquals(InetAddress.getByName("127.0.0.1"), naturalEndpoint); } diff --git a/test/unit/org/apache/cassandra/dht/LengthPartitioner.java b/test/unit/org/apache/cassandra/dht/LengthPartitioner.java index 40a6774a67..9cefbf259b 100644 --- a/test/unit/org/apache/cassandra/dht/LengthPartitioner.java +++ b/test/unit/org/apache/cassandra/dht/LengthPartitioner.java @@ -126,7 +126,7 @@ public class LengthPartitioner implements IPartitioner for (String ks : Schema.instance.getKeyspaces()) { - for (CFMetaData cfmd : Schema.instance.getTables(ks)) + for (CFMetaData cfmd : Schema.instance.getTablesAndViews(ks)) { for (Range r : sortedRanges) { diff --git a/test/unit/org/apache/cassandra/hints/HintTest.java b/test/unit/org/apache/cassandra/hints/HintTest.java index c198149493..4c7ec70fc7 100644 --- a/test/unit/org/apache/cassandra/hints/HintTest.java +++ b/test/unit/org/apache/cassandra/hints/HintTest.java @@ -67,7 +67,7 @@ public class HintTest @Before public void resetGcGraceSeconds() { - for (CFMetaData table : Schema.instance.getTables(KEYSPACE)) + for (CFMetaData table : Schema.instance.getTablesAndViews(KEYSPACE)) table.gcGraceSeconds(TableParams.DEFAULT_GC_GRACE_SECONDS); } diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java index 73eed06999..d841e919ef 100644 --- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java +++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java @@ -316,6 +316,7 @@ public class LegacySchemaMigratorTest return KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.none(), + Views.none(), Types.of(udt1, udt2, udt3), Functions.none()); } @@ -353,6 +354,7 @@ public class LegacySchemaMigratorTest return KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.none(), + Views.none(), Types.none(), Functions.of(udf1, udf2, udf3)); } @@ -365,6 +367,7 @@ public class LegacySchemaMigratorTest return KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.none(), + Views.none(), Types.none(), Functions.of()); }