mirror of https://github.com/apache/cassandra
Improve MV schema representation
patch by Carl Yeksigian; reviewed by Aleksey Yeschenko for CASSANDRA-9921
This commit is contained in:
parent
f542a20eaa
commit
a3a8dbca5c
|
|
@ -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)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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<ByteBuffer, DroppedColumn> 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<ColumnDefinition> partitionKeyColumns,
|
||||
List<ColumnDefinition> 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<Flag> 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<ColumnDefinition> 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<Pair<ColumnIdentifier, AbstractType>> staticColumns = new ArrayList<>();
|
||||
private final List<Pair<ColumnIdentifier, AbstractType>> 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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ColumnIdentifier> partitionColumns;
|
||||
public final List<ColumnIdentifier> clusteringColumns;
|
||||
public final Set<ColumnIdentifier> 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<ColumnIdentifier> partitionColumns, List<ColumnIdentifier> clusteringColumns, Set<ColumnIdentifier> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CFMetaData> 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<String, String> 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<UUID> 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<ViewDefinition> 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)));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -740,7 +740,7 @@ indexIdent returns [IndexTarget.Raw id]
|
|||
* PRIMARY KEY (<pkColumns>)
|
||||
* WITH <property> = <value> AND ...;
|
||||
*/
|
||||
createMaterializedViewStatement returns [CreateMaterializedViewStatement expr]
|
||||
createMaterializedViewStatement returns [CreateViewStatement expr]
|
||||
@init {
|
||||
boolean ifNotExists = false;
|
||||
List<ColumnIdentifier.Raw> 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] <view_name>
|
||||
*/
|
||||
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); }
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<CFMetaData> materializedViewUpdates = null;
|
||||
List<ViewDefinition> viewUpdates = null;
|
||||
Iterable<ViewDefinition> 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<RawSelector> 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<RawSelector> selectClause,
|
||||
List<ColumnIdentifier.Raw> notNullWhereClause,
|
||||
List<ColumnIdentifier.Raw> partitionKeys,
|
||||
List<ColumnIdentifier.Raw> clusteringKeys,
|
||||
boolean ifNotExists)
|
||||
public CreateViewStatement(CFName viewName,
|
||||
CFName baseName,
|
||||
List<RawSelector> selectClause,
|
||||
List<ColumnIdentifier.Raw> notNullWhereClause,
|
||||
List<ColumnIdentifier.Raw> partitionKeys,
|
||||
List<ColumnIdentifier.Raw> 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<ColumnIdentifier> 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<ColumnIdentifier> 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;
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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<ViewDefinition> 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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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<ColumnFamilyStore> selfWithAuxiliaryCfs = interruptViews
|
||||
? Iterables.concat(concatWithIndexes(), materializedViewManager.allViewsCfs())
|
||||
? Iterables.concat(concatWithIndexes(), viewManager.allViewsCfs())
|
||||
: concatWithIndexes();
|
||||
|
||||
for (ColumnFamilyStore cfs : selfWithAuxiliaryCfs)
|
||||
|
|
|
|||
|
|
@ -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<UUID, ColumnFamilyStore> columnFamilyStores = new ConcurrentHashMap<>();
|
||||
private volatile AbstractReplicationStrategy replicationStrategy;
|
||||
public final ViewManager viewManager;
|
||||
|
||||
public static final Function<String,Keyspace> keyspaceTransformer = new Function<String, Keyspace>()
|
||||
{
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<blob>,"
|
||||
+ "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<Integer, Token> getMaterializedViewBuildStatus(String ksname, String viewName)
|
||||
public static Pair<Integer, Token> 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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ public enum WriteType
|
|||
COUNTER,
|
||||
BATCH_LOG,
|
||||
CAS,
|
||||
MATERIALIZED_VIEW;
|
||||
VIEW;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Lock> LOCKS = Striped.lazyWeakLock(DatabaseDescriptor.getConcurrentWriters() * 1024);
|
||||
private static final boolean enableCoordinatorBatchlog = Boolean.getBoolean("cassandra.mv_enable_coordinator_batchlog");
|
||||
|
||||
private final ConcurrentNavigableMap<String, MaterializedView> viewsByName;
|
||||
|
||||
private final ColumnFamilyStore baseCfs;
|
||||
|
||||
public MaterializedViewManager(ColumnFamilyStore baseCfs)
|
||||
{
|
||||
this.viewsByName = new ConcurrentSkipListMap<>();
|
||||
|
||||
this.baseCfs = baseCfs;
|
||||
}
|
||||
|
||||
public Iterable<MaterializedView> allViews()
|
||||
{
|
||||
return viewsByName.values();
|
||||
}
|
||||
|
||||
public Iterable<ColumnFamilyStore> allViewsCfs()
|
||||
{
|
||||
List<ColumnFamilyStore> 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<String, MaterializedViewDefinition> 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<String, MaterializedViewDefinition> 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<Mutation> mutations = null;
|
||||
TemporalRow.Set temporalRows = null;
|
||||
for (Map.Entry<String, MaterializedView> view : viewsByName.entrySet())
|
||||
{
|
||||
|
||||
temporalRows = view.getValue().getTemporalRowSet(update, temporalRows, false);
|
||||
|
||||
Collection<Mutation> 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<? extends IMutation> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ColumnDefinition> primaryKeyDefs;
|
||||
public final List<ColumnDefinition> baseComplexColumns;
|
||||
|
||||
private MVColumns(List<ColumnDefinition> partitionDefs, List<ColumnDefinition> primaryKeyDefs, List<ColumnDefinition> baseComplexColumns)
|
||||
private Columns(List<ColumnDefinition> partitionDefs, List<ColumnDefinition> primaryKeyDefs, List<ColumnDefinition> 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<ColumnDefinition> partitionDefs = new ArrayList<>(definition.partitionColumns.size());
|
||||
List<ColumnDefinition> primaryKeyDefs = new ArrayList<>(definition.partitionColumns.size()
|
||||
+ definition.clusteringColumns.size());
|
||||
this.definition = definition;
|
||||
|
||||
CFMetaData viewCfm = definition.metadata;
|
||||
List<ColumnDefinition> partitionDefs = new ArrayList<>(viewCfm.partitionKeyColumns().size());
|
||||
List<ColumnDefinition> primaryKeyDefs = new ArrayList<>(viewCfm.partitionKeyColumns().size()
|
||||
+ viewCfm.clusteringColumns().size());
|
||||
List<ColumnDefinition> 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<ViewDefinition> 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<ColumnDefinition> 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Range<Token>> ranges = StorageService.instance.getLocalRanges(baseCfs.metadata.ksName);
|
||||
final Pair<Integer, Token> buildStatus = SystemKeyspace.getMaterializedViewBuildStatus(ksname, viewName);
|
||||
final Pair<Integer, Token> buildStatus = SystemKeyspace.getViewBuildStatus(ksname, viewName);
|
||||
Token lastToken;
|
||||
Function<View, Iterable<SSTableReader>> function;
|
||||
Function<org.apache.cassandra.db.lifecycle.View, Iterable<SSTableReader>> 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<SSTableReader> 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<View, Iterable<SSTableReader>>()
|
||||
function = new Function<org.apache.cassandra.db.lifecycle.View, Iterable<SSTableReader>>()
|
||||
{
|
||||
@Nullable
|
||||
public Iterable<SSTableReader> apply(View view)
|
||||
public Iterable<SSTableReader> apply(org.apache.cassandra.db.lifecycle.View view)
|
||||
{
|
||||
Iterable<SSTableReader> readers = View.select(SSTableSet.CANONICAL).apply(view);
|
||||
Iterable<SSTableReader> 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);
|
||||
|
|
@ -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<String, View> viewsByName;
|
||||
|
||||
public ForStore()
|
||||
{
|
||||
this.viewsByName = new ConcurrentSkipListMap<>();
|
||||
}
|
||||
|
||||
public Iterable<View> allViews()
|
||||
{
|
||||
return viewsByName.values();
|
||||
}
|
||||
|
||||
public Iterable<ColumnFamilyStore> allViewsCfs()
|
||||
{
|
||||
List<ColumnFamilyStore> 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<Lock> LOCKS = Striped.lazyWeakLock(DatabaseDescriptor.getConcurrentViewWriters() * 1024);
|
||||
|
||||
private static final boolean enableCoordinatorBatchlog = Boolean.getBoolean("cassandra.mv_enable_coordinator_batchlog");
|
||||
|
||||
private final ConcurrentNavigableMap<UUID, ForStore> viewManagersByStore;
|
||||
private final ConcurrentNavigableMap<String, View> 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<Mutation> mutations = null;
|
||||
TemporalRow.Set temporalRows = null;
|
||||
for (Map.Entry<String, View> view : viewsByName.entrySet())
|
||||
{
|
||||
temporalRows = view.getValue().getTemporalRowSet(update, temporalRows, false);
|
||||
|
||||
Collection<Mutation> 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<? extends IMutation> 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<View> 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> 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<String, ViewDefinition> 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<String, ViewDefinition> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -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<Token> r : sortedRanges)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Token> r : sortedRanges)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
@ -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<CFMetaData> 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<String> existingIndexNames(String cfToExclude)
|
||||
|
|
@ -94,7 +118,7 @@ public final class KeyspaceMetadata
|
|||
|
||||
public Optional<CFMetaData> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String> 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<map<text, text>>,"
|
||||
+ "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<list<text>>,"
|
||||
+ "clustering_columns frozen<list<text>>,"
|
||||
+ "included_columns frozen<list<text>>,"
|
||||
+ "PRIMARY KEY ((keyspace_name), table_name, view_name))");
|
||||
+ "base_table_id uuid,"
|
||||
+ "base_table_name text,"
|
||||
+ "bloom_filter_fp_chance double,"
|
||||
+ "caching frozen<map<text, text>>,"
|
||||
+ "comment text,"
|
||||
+ "compaction frozen<map<text, text>>,"
|
||||
+ "compression frozen<map<text, text>>,"
|
||||
+ "dclocal_read_repair_chance double,"
|
||||
+ "default_time_to_live int,"
|
||||
+ "extensions frozen<map<text, blob>>,"
|
||||
+ "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<CFMetaData> 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<DecoratedKey, FilteredPartition> oldKeyspaces = readSchemaForKeyspaces(KEYSPACES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> oldColumnFamilies = readSchemaForKeyspaces(TABLES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> oldViews = readSchemaForKeyspaces(VIEWS, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> oldTypes = readSchemaForKeyspaces(TYPES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> oldFunctions = readSchemaForKeyspaces(FUNCTIONS, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> oldAggregates = readSchemaForKeyspaces(AGGREGATES, keyspaces);
|
||||
|
|
@ -485,12 +501,14 @@ public final class SchemaKeyspace
|
|||
// with new data applied
|
||||
Map<DecoratedKey, FilteredPartition> newKeyspaces = readSchemaForKeyspaces(KEYSPACES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> newColumnFamilies = readSchemaForKeyspaces(TABLES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> newViews = readSchemaForKeyspaces(VIEWS, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> newTypes = readSchemaForKeyspaces(TYPES, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> newFunctions = readSchemaForKeyspaces(FUNCTIONS, keyspaces);
|
||||
Map<DecoratedKey, FilteredPartition> newAggregates = readSchemaForKeyspaces(AGGREGATES, keyspaces);
|
||||
|
||||
Set<String> 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<DecoratedKey, FilteredPartition> before, Map<DecoratedKey, FilteredPartition> 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<DecoratedKey, FilteredPartition> before, Map<DecoratedKey, FilteredPartition> 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<UDFunction> udfs = createFunctionsFromFunctionsPartition(serializedFunctions);
|
||||
Collection<UDAggregate> 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<String, MaterializedViewDefinition> 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<MaterializedViewDefinition> diff : materializedViewDiff.entriesDiffering().values())
|
||||
{
|
||||
addUpdatedMaterializedViewDefinitionToSchemaMutation(newTable, diff.rightValue(), timestamp, mutation);
|
||||
}
|
||||
|
||||
MapDifference<String, IndexMetadata> indexesDiff = indexesDiff(oldTable.getIndexes(),
|
||||
newTable.getIndexes());
|
||||
|
||||
|
|
@ -989,17 +1012,6 @@ public final class SchemaKeyspace
|
|||
return Maps.difference(beforeMap, afterMap);
|
||||
}
|
||||
|
||||
private static MapDifference<String, MaterializedViewDefinition> materializedViewsDiff(MaterializedViews before, MaterializedViews after)
|
||||
{
|
||||
Map<String, MaterializedViewDefinition> beforeMap = new HashMap<>();
|
||||
before.forEach(v -> beforeMap.put(v.viewName, v));
|
||||
|
||||
Map<String, MaterializedViewDefinition> 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<ByteBuffer, ColumnDefinition> 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<ByteBuffer, CFMetaData.DroppedColumn> 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<String> 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<String> clusteringColumnNames = row.getFrozenList("clustering_columns", UTF8Type.instance);
|
||||
List<ColumnDefinition> columns =
|
||||
readSchemaPartitionForTableAndApply(COLUMNS, keyspace, view, SchemaKeyspace::createColumnsFromColumnsPartition);
|
||||
|
||||
List<ColumnIdentifier> partitionColumns = new ArrayList<>();
|
||||
for (String columnName : partitionColumnNames)
|
||||
{
|
||||
partitionColumns.add(ColumnIdentifier.getInterned(columnName, true));
|
||||
}
|
||||
Map<ByteBuffer, CFMetaData.DroppedColumn> droppedColumns =
|
||||
readSchemaPartitionForTableAndApply(DROPPED_COLUMNS, keyspace, view, SchemaKeyspace::createDroppedColumnsFromDroppedColumnsPartition);
|
||||
|
||||
List<ColumnIdentifier> 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<String> includedColumnNames = row.getFrozenList("included_columns", UTF8Type.instance);
|
||||
Set<ColumnIdentifier> 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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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<MaterializedViewDefinition>
|
||||
public final class Views implements Iterable<ViewDefinition>
|
||||
{
|
||||
private final ImmutableMap<String, MaterializedViewDefinition> materializedViews;
|
||||
private final ImmutableMap<String, ViewDefinition> 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<MaterializedViewDefinit
|
|||
return new Builder();
|
||||
}
|
||||
|
||||
public static MaterializedViews none()
|
||||
public static Views none()
|
||||
{
|
||||
return builder().build();
|
||||
}
|
||||
|
||||
public Iterator<MaterializedViewDefinition> iterator()
|
||||
public Iterator<ViewDefinition> iterator()
|
||||
{
|
||||
return materializedViews.values().iterator();
|
||||
return views.values().iterator();
|
||||
}
|
||||
|
||||
public Iterable<CFMetaData> 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<MaterializedViewDefinition> get(String name)
|
||||
public Optional<ViewDefinition> 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<MaterializedViewDefinit
|
|||
/**
|
||||
* Creates a MaterializedViews instance which contains an updated materialized view
|
||||
*/
|
||||
public MaterializedViews replace(MaterializedViewDefinition materializedView)
|
||||
public Views replace(ViewDefinition view, CFMetaData cfm)
|
||||
{
|
||||
return without(materializedView.viewName).with(materializedView);
|
||||
return without(view.viewName).with(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return this == o || (o instanceof MaterializedViews && materializedViews.equals(((MaterializedViews) o).materializedViews));
|
||||
return this == o || (o instanceof Views && views.equals(((Views) o).views));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return materializedViews.hashCode();
|
||||
return views.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return materializedViews.values().toString();
|
||||
return views.values().toString();
|
||||
}
|
||||
|
||||
public static final class Builder
|
||||
{
|
||||
final ImmutableMap.Builder<String, MaterializedViewDefinition> materializedViews = new ImmutableMap.Builder<>();
|
||||
final ImmutableMap.Builder<String, ViewDefinition> 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<MaterializedViewDefinition> materializedViews)
|
||||
public Builder add(Iterable<ViewDefinition> views)
|
||||
{
|
||||
materializedViews.forEach(this::add);
|
||||
views.forEach(this::add);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Mutation> 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<InetAddress> 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<Mutation> 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<InetAddress> naturalEndpoints,
|
||||
WriteType writeType,
|
||||
BatchlogResponseHandler.BatchlogCleanup cleanup)
|
||||
private static WriteResponseHandlerWrapper wrapViewBatchResponseHandler(Mutation mutation,
|
||||
ConsistencyLevel consistency_level,
|
||||
ConsistencyLevel batchConsistencyLevel,
|
||||
List<InetAddress> 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<InetAddress> pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName);
|
||||
AbstractWriteResponseHandler<IMutation> writeHandler = rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, null, writeType);
|
||||
BatchlogResponseHandler<IMutation> batchHandler = new MVWriteMetricsWrapped(writeHandler, batchConsistencyLevel.blockFor(keyspace), cleanup);
|
||||
BatchlogResponseHandler<IMutation> 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<IMutation>
|
||||
private static class ViewWriteMetricsWrapped extends BatchlogResponseHandler<IMutation>
|
||||
{
|
||||
public MVWriteMetricsWrapped(AbstractWriteResponseHandler<IMutation> writeHandler, int i, BatchlogCleanup cleanup)
|
||||
public ViewWriteMetricsWrapped(AbstractWriteResponseHandler<IMutation> writeHandler, int i, BatchlogCleanup cleanup)
|
||||
{
|
||||
super(writeHandler, i, cleanup);
|
||||
mvWriteMetrics.viewReplicasAttempted.inc(totalEndpoints());
|
||||
viewWriteMetrics.viewReplicasAttempted.inc(totalEndpoints());
|
||||
}
|
||||
|
||||
public void response(MessageIn<IMutation> msg)
|
||||
{
|
||||
super.response(msg);
|
||||
mvWriteMetrics.viewReplicasSuccess.inc();
|
||||
viewWriteMetrics.viewReplicasSuccess.inc();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SSTableReader> 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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ public class ThriftConversion
|
|||
public static KsDef toThrift(KeyspaceMetadata ksm)
|
||||
{
|
||||
List<CfDef> 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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CFMetaData.Flag> 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<ColumnDefinition> 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<String, CFMetaData> fetchViewMetadata(String keyspace, Session session, IPartitioner partitioner)
|
||||
{
|
||||
Map<String, CFMetaData> 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<CFMetaData.Flag> 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<ColumnDefinition> 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"));
|
||||
|
|
|
|||
|
|
@ -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<String> materializedViews = new ArrayList<>();
|
||||
private final List<String> 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)
|
||||
|
|
@ -309,7 +309,7 @@ public class SchemaLoader
|
|||
private static void useCompression(List<KeyspaceMetadata> schema)
|
||||
{
|
||||
for (KeyspaceMetadata ksm : schema)
|
||||
for (CFMetaData cfm : ksm.tables)
|
||||
for (CFMetaData cfm : ksm.tablesAndViews())
|
||||
cfm.compression(CompressionParams.snappy());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<String> materializedViews = new ArrayList<>();
|
||||
private final List<String> 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<Row> 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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<Token> r : sortedRanges)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue