/*
 * 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.
 */
package com.datastax.oss.driver.internal.core.metadata.schema.parsing;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.AggregateMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.FunctionMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature;
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.ViewMetadata;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.internal.core.adminrequest.AdminRow;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.metadata.schema.DefaultKeyspaceMetadata;
import com.datastax.oss.driver.internal.core.metadata.schema.queries.SchemaRows;
import com.datastax.oss.driver.internal.core.metadata.schema.refresh.SchemaRefresh;
import com.datastax.oss.driver.internal.core.util.NanoTime;
import com.datastax.oss.driver.shaded.guava.common.base.MoreObjects;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.Map;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Default parser implementation for Cassandra.

For modularity, the code for each element row is split into separate classes (schema stuff is not on the hot path, so creating a few extra objects doesn't matter).

/** * Default parser implementation for Cassandra. * * <p>For modularity, the code for each element row is split into separate classes (schema stuff is * not on the hot path, so creating a few extra objects doesn't matter). */
@ThreadSafe public class CassandraSchemaParser implements SchemaParser { private static final Logger LOG = LoggerFactory.getLogger(CassandraSchemaParser.class); private final SchemaRows rows; private final UserDefinedTypeParser userDefinedTypeParser; private final TableParser tableParser; private final ViewParser viewParser; private final FunctionParser functionParser; private final AggregateParser aggregateParser; private final String logPrefix; private final long startTimeNs = System.nanoTime(); public CassandraSchemaParser(SchemaRows rows, InternalDriverContext context) { this.rows = rows; this.logPrefix = context.getSessionName(); this.userDefinedTypeParser = new UserDefinedTypeParser(rows.dataTypeParser(), context); this.tableParser = new TableParser(rows, context); this.viewParser = new ViewParser(rows, context); this.functionParser = new FunctionParser(rows.dataTypeParser(), context); this.aggregateParser = new AggregateParser(rows.dataTypeParser(), context); } @Override public SchemaRefresh parse() { ImmutableMap.Builder<CqlIdentifier, KeyspaceMetadata> keyspacesBuilder = ImmutableMap.builder(); for (AdminRow row : rows.keyspaces()) { KeyspaceMetadata keyspace = parseKeyspace(row); keyspacesBuilder.put(keyspace.getName(), keyspace); } for (AdminRow row : rows.virtualKeyspaces()) { KeyspaceMetadata keyspace = parseVirtualKeyspace(row); keyspacesBuilder.put(keyspace.getName(), keyspace); } SchemaRefresh refresh = new SchemaRefresh(keyspacesBuilder.build()); LOG.debug("[{}] Schema parsing took {}", logPrefix, NanoTime.formatTimeSince(startTimeNs)); return refresh; } private KeyspaceMetadata parseKeyspace(AdminRow keyspaceRow) { // Cassandra <= 2.2 // CREATE TABLE system.schema_keyspaces ( // keyspace_name text PRIMARY KEY, // durable_writes boolean, // strategy_class text, // strategy_options text // ) // // Cassandra >= 3.0: // CREATE TABLE system_schema.keyspaces ( // keyspace_name text PRIMARY KEY, // durable_writes boolean, // replication frozen<map<text, text>> // ) CqlIdentifier keyspaceId = CqlIdentifier.fromInternal(keyspaceRow.getString("keyspace_name")); boolean durableWrites = MoreObjects.firstNonNull(keyspaceRow.getBoolean("durable_writes"), false); Map<String, String> replicationOptions; if (keyspaceRow.contains("strategy_class")) { String strategyClass = keyspaceRow.getString("strategy_class"); Map<String, String> strategyOptions = SimpleJsonParser.parseStringMap(keyspaceRow.getString("strategy_options")); replicationOptions = ImmutableMap.<String, String>builder() .putAll(strategyOptions) .put("class", strategyClass) .build(); } else { replicationOptions = keyspaceRow.getMapOfStringToString("replication"); } Map<CqlIdentifier, UserDefinedType> types = parseTypes(keyspaceId); return new DefaultKeyspaceMetadata( keyspaceId, durableWrites, false, replicationOptions, types, parseTables(keyspaceId, types), parseViews(keyspaceId, types), parseFunctions(keyspaceId, types), parseAggregates(keyspaceId, types)); } private KeyspaceMetadata parseVirtualKeyspace(AdminRow keyspaceRow) { CqlIdentifier keyspaceId = CqlIdentifier.fromInternal(keyspaceRow.getString("keyspace_name")); boolean durableWrites = MoreObjects.firstNonNull(keyspaceRow.getBoolean("durable_writes"), false); Map<String, String> replicationOptions = Collections.emptyMap(); ; Map<CqlIdentifier, UserDefinedType> types = parseTypes(keyspaceId); return new DefaultKeyspaceMetadata( keyspaceId, durableWrites, true, replicationOptions, types, parseVirtualTables(keyspaceId, types), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); } private Map<CqlIdentifier, UserDefinedType> parseTypes(CqlIdentifier keyspaceId) { return userDefinedTypeParser.parse(rows.types().get(keyspaceId), keyspaceId); } private Map<CqlIdentifier, TableMetadata> parseVirtualTables( CqlIdentifier keyspaceId, Map<CqlIdentifier, UserDefinedType> types) { ImmutableMap.Builder<CqlIdentifier, TableMetadata> tablesBuilder = ImmutableMap.builder(); for (AdminRow tableRow : rows.virtualTables().get(keyspaceId)) { TableMetadata table = tableParser.parseVirtualTable(tableRow, keyspaceId, types); if (table != null) { tablesBuilder.put(table.getName(), table); } } return tablesBuilder.build(); } private Map<CqlIdentifier, TableMetadata> parseTables( CqlIdentifier keyspaceId, Map<CqlIdentifier, UserDefinedType> types) { ImmutableMap.Builder<CqlIdentifier, TableMetadata> tablesBuilder = ImmutableMap.builder(); for (AdminRow tableRow : rows.tables().get(keyspaceId)) { TableMetadata table = tableParser.parseTable(tableRow, keyspaceId, types); if (table != null) { tablesBuilder.put(table.getName(), table); } } return tablesBuilder.build(); } private Map<CqlIdentifier, ViewMetadata> parseViews( CqlIdentifier keyspaceId, Map<CqlIdentifier, UserDefinedType> types) { ImmutableMap.Builder<CqlIdentifier, ViewMetadata> viewsBuilder = ImmutableMap.builder(); for (AdminRow viewRow : rows.views().get(keyspaceId)) { ViewMetadata view = viewParser.parseView(viewRow, keyspaceId, types); if (view != null) { viewsBuilder.put(view.getName(), view); } } return viewsBuilder.build(); } private Map<FunctionSignature, FunctionMetadata> parseFunctions( CqlIdentifier keyspaceId, Map<CqlIdentifier, UserDefinedType> types) { ImmutableMap.Builder<FunctionSignature, FunctionMetadata> functionsBuilder = ImmutableMap.builder(); for (AdminRow functionRow : rows.functions().get(keyspaceId)) { FunctionMetadata function = functionParser.parseFunction(functionRow, keyspaceId, types); if (function != null) { functionsBuilder.put(function.getSignature(), function); } } return functionsBuilder.build(); } private Map<FunctionSignature, AggregateMetadata> parseAggregates( CqlIdentifier keyspaceId, Map<CqlIdentifier, UserDefinedType> types) { ImmutableMap.Builder<FunctionSignature, AggregateMetadata> aggregatesBuilder = ImmutableMap.builder(); for (AdminRow aggregateRow : rows.aggregates().get(keyspaceId)) { AggregateMetadata aggregate = aggregateParser.parseAggregate(aggregateRow, keyspaceId, types); if (aggregate != null) { aggregatesBuilder.put(aggregate.getSignature(), aggregate); } } return aggregatesBuilder.build(); } }