com.datastax.driver.core.KeyspaceMetadata Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* Licensed 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.
*
* The following only applies to changes made to this file as part of YugaByte development.
*
* Portions Copyright (c) YugaByte, Inc.
*
* Licensed 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 com.datastax.driver.core;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** Describes a keyspace defined in this cluster. */
public class KeyspaceMetadata {
public static final String KS_NAME = "keyspace_name";
private static final String DURABLE_WRITES = "durable_writes";
private static final String STRATEGY_CLASS = "strategy_class";
private static final String STRATEGY_OPTIONS = "strategy_options";
private static final String REPLICATION = "replication";
private static final Set SYSTEM_KEYSPACES =
ImmutableSet.of(
"system", "system_auth", "system_distributed", "system_schema", "system_traces");
private final String name;
private final boolean durableWrites;
private final boolean isSystem;
private final boolean virtual;
private final ReplicationStrategy strategy;
private final Map replication;
final Map tables = new ConcurrentHashMap();
final Map views =
new ConcurrentHashMap();
final Map userTypes =
Collections.synchronizedMap(new LinkedHashMap());
final Map functions = new ConcurrentHashMap();
final Map aggregates =
new ConcurrentHashMap();
@VisibleForTesting
@Deprecated
KeyspaceMetadata(String name, boolean durableWrites, Map replication) {
this(name, durableWrites, replication, false);
}
@VisibleForTesting
KeyspaceMetadata(
String name, boolean durableWrites, Map replication, boolean virtual) {
this.name = name;
this.durableWrites = durableWrites;
this.replication = replication;
this.strategy = ReplicationStrategy.create(replication);
this.isSystem = isSystem(name);
this.virtual = virtual;
}
static KeyspaceMetadata build(Row row, VersionNumber cassandraVersion) {
if (cassandraVersion.getMajor() <= 2) {
String name = row.getString(KS_NAME);
boolean durableWrites = row.getBool(DURABLE_WRITES);
Map replicationOptions;
replicationOptions = new HashMap();
replicationOptions.put("class", row.getString(STRATEGY_CLASS));
replicationOptions.putAll(SimpleJSONParser.parseStringMap(row.getString(STRATEGY_OPTIONS)));
return new KeyspaceMetadata(name, durableWrites, replicationOptions, false);
} else {
String name = row.getString(KS_NAME);
boolean durableWrites = row.getBool(DURABLE_WRITES);
return new KeyspaceMetadata(
name, durableWrites, row.getMap(REPLICATION, String.class, String.class), false);
}
}
/**
* Returns whether keyspace is a system keyspace.
*
* @param keyspaceName the keyspace name
* @return {@code true} if keyspaceName is one of the system keyspaces, {@code false} otherwise.
*/
public static boolean isSystem(String keyspaceName) {
return SYSTEM_KEYSPACES.contains(keyspaceName);
}
static KeyspaceMetadata buildVirtual(Row row, VersionNumber cassandraVersion) {
String name = row.getString(KS_NAME);
return new KeyspaceMetadata(name, false, Collections.emptyMap(), true);
}
/**
* Returns the name of this keyspace.
*
* @return the name of this CQL keyspace.
*/
public String getName() {
return name;
}
/**
* Returns whether durable writes are set on this keyspace.
*
* @return {@code true} if durable writes are set on this keyspace (the default), {@code false}
* otherwise.
*/
public boolean isDurableWrites() {
return durableWrites;
}
/**
* Returns whether or not this keyspace is a virtual keyspace
*
* @return {@code true} if virtual keyspace default), {@code false} otherwise.
*/
public boolean isVirtual() {
return virtual;
}
/**
* Returns the replication options for this keyspace.
*
* @return a map containing the replication options for this keyspace.
*/
public Map getReplication() {
return Collections.unmodifiableMap(replication);
}
/**
* Returns the metadata for a table contained in this keyspace.
*
* @param name the name of table to retrieve
* @return the metadata for table {@code name} if it exists in this keyspace, {@code null}
* otherwise.
*/
public TableMetadata getTable(String name) {
return tables.get(Metadata.handleId(name));
}
TableMetadata removeTable(String table) {
return tables.remove(table);
}
/**
* Returns the tables defined in this keyspace.
*
* @return a collection of the metadata for the tables defined in this keyspace.
*/
public Collection getTables() {
return Collections.unmodifiableCollection(tables.values());
}
/**
* Returns the metadata for a materialized view contained in this keyspace.
*
* @param name the name of materialized view to retrieve
* @return the metadata for materialized view {@code name} if it exists in this keyspace, {@code
* null} otherwise.
*/
public MaterializedViewMetadata getMaterializedView(String name) {
return views.get(Metadata.handleId(name));
}
MaterializedViewMetadata removeMaterializedView(String materializedView) {
return views.remove(materializedView);
}
/**
* Returns the materialized views defined in this keyspace.
*
* @return a collection of the metadata for the materialized views defined in this keyspace.
*/
public Collection getMaterializedViews() {
return Collections.unmodifiableCollection(views.values());
}
/**
* Returns the definition for a user defined type (UDT) in this keyspace.
*
* @param name the name of UDT definition to retrieve
* @return the definition for {@code name} if it exists in this keyspace, {@code null} otherwise.
*/
public UserType getUserType(String name) {
return userTypes.get(Metadata.handleId(name));
}
/**
* Returns the user types defined in this keyspace.
*
* @return a collection of the definition for the user types defined in this keyspace.
*/
public Collection getUserTypes() {
return Collections.unmodifiableCollection(userTypes.values());
}
UserType removeUserType(String userType) {
return userTypes.remove(userType);
}
/**
* Returns the definition of a function in this keyspace.
*
* @param name the name of the function.
* @param argumentTypes the types of the function's arguments.
* @return the function definition if it exists in this keyspace, {@code null} otherwise.
*/
public FunctionMetadata getFunction(String name, Collection argumentTypes) {
return functions.get(Metadata.fullFunctionName(Metadata.handleId(name), argumentTypes));
}
/**
* Returns the definition of a function in this keyspace.
*
* @param name the name of the function.
* @param argumentTypes the types of the function's arguments.
* @return the function definition if it exists in this keyspace, {@code null} otherwise.
*/
public FunctionMetadata getFunction(String name, DataType... argumentTypes) {
return getFunction(name, Lists.newArrayList(argumentTypes));
}
/**
* Returns the functions defined in this keyspace.
*
* @return a collection of the definition for the functions defined in this keyspace.
*/
public Collection getFunctions() {
return Collections.unmodifiableCollection(functions.values());
}
FunctionMetadata removeFunction(String fullName) {
return functions.remove(fullName);
}
/**
* Returns the definition of an aggregate in this keyspace.
*
* @param name the name of the aggregate.
* @param argumentTypes the types of the aggregate's arguments.
* @return the aggregate definition if it exists in this keyspace, {@code null} otherwise.
*/
public AggregateMetadata getAggregate(String name, Collection argumentTypes) {
return aggregates.get(Metadata.fullFunctionName(Metadata.handleId(name), argumentTypes));
}
/**
* Returns the definition of an aggregate in this keyspace.
*
* @param name the name of the aggregate.
* @param argumentTypes the types of the aggregate's arguments.
* @return the aggregate definition if it exists in this keyspace, {@code null} otherwise.
*/
public AggregateMetadata getAggregate(String name, DataType... argumentTypes) {
return getAggregate(name, Lists.newArrayList(argumentTypes));
}
/**
* Returns the aggregates defined in this keyspace.
*
* @return a collection of the definition for the aggregates defined in this keyspace.
*/
public Collection getAggregates() {
return Collections.unmodifiableCollection(aggregates.values());
}
/**
* Returns whether this keyspace is a system keyspace.
*
* @return {@code true} if this is a system keyspace, and {@code false} otherwise
*/
public boolean isSystem() {
return isSystem;
}
AggregateMetadata removeAggregate(String fullName) {
return aggregates.remove(fullName);
}
// comparators for ordering types in cqlsh output.
private static final Comparator typeByName =
new Comparator() {
@Override
public int compare(UserType o1, UserType o2) {
return o1.getTypeName().compareTo(o2.getTypeName());
}
};
private static final Comparator functionByName =
new Comparator() {
@Override
public int compare(FunctionMetadata o1, FunctionMetadata o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
};
private static final Comparator aggregateByName =
new Comparator() {
@Override
public int compare(AggregateMetadata o1, AggregateMetadata o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
};
/**
* Returns a {@code String} containing CQL queries representing this keyspace and the user types
* and tables it contains.
*
* In other words, this method returns the queries that would allow to recreate the schema of
* this keyspace, along with all its user types/tables.
*
*
Note that the returned String is formatted to be human readable (for some definition of
* human readable at least).
*
* @return the CQL queries representing this keyspace schema as a {code String}.
*/
public String exportAsString() {
StringBuilder sb = new StringBuilder();
sb.append(asCQLQuery()).append('\n');
// include types, tables, views, functions and aggregates, each ordered by name, with one small
// exception
// being that user types are ordered topologically and then by name within same level.
for (UserType udt : getSortedUserTypes())
sb.append('\n').append(udt.exportAsString()).append('\n');
for (AbstractTableMetadata tm :
ImmutableSortedSet.orderedBy(AbstractTableMetadata.byNameComparator)
.addAll(tables.values())
.build()) sb.append('\n').append(tm.exportAsString()).append('\n');
for (FunctionMetadata fm :
ImmutableSortedSet.orderedBy(functionByName).addAll(functions.values()).build())
sb.append('\n').append(fm.exportAsString()).append('\n');
for (AggregateMetadata am :
ImmutableSortedSet.orderedBy(aggregateByName).addAll(aggregates.values()).build())
sb.append('\n').append(am.exportAsString()).append('\n');
return sb.toString();
}
private List getSortedUserTypes() {
// rebuilds dependency tree of user types so they may be sorted within each dependency level.
List unsortedTypes = new ArrayList(userTypes.values());
DirectedGraph graph = new DirectedGraph(typeByName, unsortedTypes);
for (UserType from : unsortedTypes) {
for (UserType to : unsortedTypes) {
if (from != to && dependsOn(to, from)) graph.addEdge(from, to);
}
}
return graph.topologicalSort();
}
private boolean dependsOn(UserType udt1, UserType udt2) {
for (UserType.Field field : udt1) {
if (references(field.getType(), udt2)) {
return true;
}
}
return false;
}
private boolean references(DataType dataType, DataType udtType) {
if (dataType.equals(udtType)) return true;
for (DataType arg : dataType.getTypeArguments()) {
if (references(arg, udtType)) return true;
}
if (dataType instanceof TupleType) {
for (DataType arg : ((TupleType) dataType).getComponentTypes()) {
if (references(arg, udtType)) return true;
}
}
return false;
}
/**
* Returns a CQL query representing this keyspace.
*
* This method returns a single 'CREATE KEYSPACE' query with the options corresponding to this
* keyspace definition.
*
* @return the 'CREATE KEYSPACE' query corresponding to this keyspace.
* @see #exportAsString
*/
public String asCQLQuery() {
StringBuilder sb = new StringBuilder();
if (virtual) {
sb.append("/* VIRTUAL ");
} else {
sb.append("CREATE ");
}
sb.append("KEYSPACE ").append(Metadata.quoteIfNecessary(name)).append(" WITH ");
sb.append("REPLICATION = { 'class' : '").append(replication.get("class")).append('\'');
for (Map.Entry entry : replication.entrySet()) {
if (entry.getKey().equals("class")) continue;
sb.append(", '").append(entry.getKey()).append("': '").append(entry.getValue()).append('\'');
}
sb.append(" } AND DURABLE_WRITES = ").append(durableWrites);
sb.append(';');
if (virtual) {
sb.append("*/");
}
return sb.toString();
}
@Override
public String toString() {
if (virtual) {
return name;
}
return asCQLQuery();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyspaceMetadata that = (KeyspaceMetadata) o;
if (durableWrites != that.durableWrites) return false;
if (!name.equals(that.name)) return false;
if (strategy != null ? !strategy.equals(that.strategy) : that.strategy != null) return false;
if (!replication.equals(that.replication)) return false;
return tables.equals(that.tables);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (durableWrites ? 1 : 0);
result = 31 * result + (strategy != null ? strategy.hashCode() : 0);
result = 31 * result + replication.hashCode();
result = 31 * result + tables.hashCode();
return result;
}
void add(TableMetadata tm) {
tables.put(tm.getName(), tm);
}
void add(MaterializedViewMetadata view) {
views.put(view.getName(), view);
}
void add(FunctionMetadata function) {
String functionName =
Metadata.fullFunctionName(function.getSimpleName(), function.getArguments().values());
functions.put(functionName, function);
}
void add(AggregateMetadata aggregate) {
String aggregateName =
Metadata.fullFunctionName(aggregate.getSimpleName(), aggregate.getArgumentTypes());
aggregates.put(aggregateName, aggregate);
}
void add(UserType type) {
userTypes.put(type.getTypeName(), type);
}
ReplicationStrategy replicationStrategy() {
return strategy;
}
}