blob: a5642784b502f2a7da616c4dfe6883e14fa750ef [file] [log] [blame]
/*
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.
*/
#ifndef DATASTAX_INTERNAL_SCHEMA_METADATA_HPP
#define DATASTAX_INTERNAL_SCHEMA_METADATA_HPP
#include "allocated.hpp"
#include "copy_on_write_ptr.hpp"
#include "data_type.hpp"
#include "external.hpp"
#include "host.hpp"
#include "iterator.hpp"
#include "macros.hpp"
#include "map.hpp"
#include "ref_counted.hpp"
#include "scoped_lock.hpp"
#include "scoped_ptr.hpp"
#include "string.hpp"
#include "value.hpp"
#include "vector.hpp"
#include <uv.h>
namespace datastax { namespace internal { namespace core {
class ColumnMetadata;
class TableMetadata;
class KeyspaceMetadata;
class Row;
class ResultResponse;
template <class T>
class MapIteratorImpl {
public:
typedef T ItemType;
typedef internal::Map<String, T> Collection;
MapIteratorImpl(const Collection& map)
: next_(map.begin())
, end_(map.end()) {}
bool next() {
if (next_ == end_) {
return false;
}
current_ = next_++;
return true;
}
const T& item() const { return current_->second; }
private:
typename Collection::const_iterator next_;
typename Collection::const_iterator current_;
typename Collection::const_iterator end_;
};
template <class T>
class VecIteratorImpl {
public:
typedef T ItemType;
typedef Vector<T> Collection;
VecIteratorImpl(const Collection& vec)
: next_(vec.begin())
, end_(vec.end()) {}
bool next() {
if (next_ == end_) {
return false;
}
current_ = next_++;
return true;
}
const T& item() const { return (*current_); }
private:
typename Collection::const_iterator next_;
typename Collection::const_iterator current_;
typename Collection::const_iterator end_;
};
class MetadataField {
public:
typedef internal::Map<String, MetadataField> Map;
MetadataField() {}
MetadataField(const String& name)
: name_(name) {}
MetadataField(const String& name, const Value& value, const RefBuffer::Ptr& buffer)
: name_(name)
, value_(value)
, buffer_(buffer) {}
const String& name() const { return name_; }
const Value* value() const { return &value_; }
private:
String name_;
Value value_;
RefBuffer::Ptr buffer_;
};
class MetadataFieldIterator : public Iterator {
public:
typedef MapIteratorImpl<MetadataField>::Collection Map;
MetadataFieldIterator(const Map& map)
: Iterator(CASS_ITERATOR_TYPE_META_FIELD)
, impl_(map) {}
virtual bool next() { return impl_.next(); }
const MetadataField* field() const { return &impl_.item(); }
private:
MapIteratorImpl<MetadataField> impl_;
};
class MetadataBase {
public:
MetadataBase(const String& name)
: name_(name) {}
const String& name() const { return name_; }
const Value* get_field(const String& name) const;
String get_string_field(const String& name) const;
Iterator* iterator_fields() const { return new MetadataFieldIterator(fields_); }
void swap_fields(MetadataBase& meta) { fields_.swap(meta.fields_); }
protected:
const Value* add_field(const RefBuffer::Ptr& buffer, const Row* row, const String& name);
void add_field(const RefBuffer::Ptr& buffer, const Value& value, const String& name);
void add_json_list_field(const Row* row, const String& name);
const Value* add_json_map_field(const Row* row, const String& name);
MetadataField::Map fields_;
private:
const String name_;
};
template <class IteratorImpl>
class MetadataIteratorImpl : public Iterator {
public:
typedef typename IteratorImpl::Collection Collection;
MetadataIteratorImpl(CassIteratorType type, const Collection& colleciton)
: Iterator(type)
, impl_(colleciton) {}
virtual bool next() { return impl_.next(); }
protected:
IteratorImpl impl_;
};
class FunctionMetadata
: public MetadataBase
, public RefCounted<FunctionMetadata> {
public:
typedef SharedRefPtr<FunctionMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
struct Argument {
typedef Vector<Argument> Vec;
Argument(const StringRef& name, const DataType::ConstPtr& type)
: name(name)
, type(type) {}
StringRef name;
DataType::ConstPtr type;
};
FunctionMetadata(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const String& name, const Value* signature, KeyspaceMetadata* keyspace,
const RefBuffer::Ptr& buffer, const Row* row);
const String& simple_name() const { return simple_name_; }
const Argument::Vec& args() const { return args_; }
const DataType::ConstPtr& return_type() const { return return_type_; }
StringRef body() const { return body_; }
StringRef language() const { return language_; }
bool called_on_null_input() const { return called_on_null_input_; }
const DataType* get_arg_type(StringRef name) const;
private:
String simple_name_;
Argument::Vec args_;
DataType::ConstPtr return_type_;
StringRef body_;
StringRef language_;
bool called_on_null_input_;
};
inline bool operator==(const FunctionMetadata::Argument& a, StringRef b) { return a.name == b; }
class AggregateMetadata
: public MetadataBase
, public RefCounted<AggregateMetadata> {
public:
typedef SharedRefPtr<AggregateMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
AggregateMetadata(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const String& name, const Value* signature, KeyspaceMetadata* keyspace,
const RefBuffer::Ptr& buffer, const Row* row);
const String& simple_name() const { return simple_name_; }
const DataType::Vec arg_types() const { return arg_types_; }
const DataType::ConstPtr& return_type() const { return return_type_; }
const DataType::ConstPtr& state_type() const { return state_type_; }
const FunctionMetadata::Ptr& state_func() const { return state_func_; }
const FunctionMetadata::Ptr& final_func() const { return final_func_; }
const Value& init_cond() const { return init_cond_; }
private:
String simple_name_;
DataType::Vec arg_types_;
DataType::ConstPtr return_type_;
DataType::ConstPtr state_type_;
FunctionMetadata::Ptr state_func_;
FunctionMetadata::Ptr final_func_;
Value init_cond_;
};
class IndexMetadata
: public MetadataBase
, public RefCounted<IndexMetadata> {
public:
typedef SharedRefPtr<IndexMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
CassIndexType type() const { return type_; }
const String& target() const { return target_; }
const Value* options() const { return &options_; }
IndexMetadata(const String& index_name)
: MetadataBase(index_name)
, type_(CASS_INDEX_TYPE_UNKNOWN) {}
static IndexMetadata::Ptr from_row(const String& index_name, const RefBuffer::Ptr& buffer,
const Row* row);
void update(StringRef index_type, const Value* options);
static IndexMetadata::Ptr from_legacy(const String& index_name, const ColumnMetadata* column,
const RefBuffer::Ptr& buffer, const Row* row);
void update_legacy(StringRef index_type, const ColumnMetadata* column, const Value* options);
private:
static CassIndexType index_type_from_string(StringRef index_type);
static String target_from_legacy(const ColumnMetadata* column, const Value* options);
private:
CassIndexType type_;
String target_;
Value options_;
private:
DISALLOW_COPY_AND_ASSIGN(IndexMetadata);
};
class ColumnMetadata
: public MetadataBase
, public RefCounted<ColumnMetadata> {
public:
typedef SharedRefPtr<ColumnMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
ColumnMetadata(const String& name)
: MetadataBase(name)
, type_(CASS_COLUMN_TYPE_REGULAR)
, position_(0)
, is_reversed_(false) {}
ColumnMetadata(const String& name, int32_t position, CassColumnType type,
const DataType::ConstPtr& data_type)
: MetadataBase(name)
, type_(type)
, position_(position)
, data_type_(data_type)
, is_reversed_(false) {}
ColumnMetadata(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const String& name, KeyspaceMetadata* keyspace, const RefBuffer::Ptr& buffer,
const Row* row);
CassColumnType type() const { return type_; }
int32_t position() const { return position_; }
const DataType::ConstPtr& data_type() const { return data_type_; }
bool is_reversed() const { return is_reversed_; }
private:
CassColumnType type_;
int32_t position_;
DataType::ConstPtr data_type_;
bool is_reversed_;
private:
DISALLOW_COPY_AND_ASSIGN(ColumnMetadata);
};
inline bool operator==(const ColumnMetadata::Ptr& a, const String& b) { return a->name() == b; }
class TableMetadataBase
: public MetadataBase
, public RefCounted<TableMetadataBase> {
public:
typedef SharedRefPtr<TableMetadataBase> Ptr;
typedef Vector<CassClusteringOrder> ClusteringOrderVec;
class ColumnIterator : public MetadataIteratorImpl<VecIteratorImpl<ColumnMetadata::Ptr> > {
public:
ColumnIterator(const ColumnIterator::Collection& collection)
: MetadataIteratorImpl<VecIteratorImpl<ColumnMetadata::Ptr> >(
CASS_ITERATOR_TYPE_COLUMN_META, collection) {}
const ColumnMetadata* column() const { return impl_.item().get(); }
};
TableMetadataBase(const VersionNumber& server_version, const String& name,
const RefBuffer::Ptr& buffer, const Row* row, bool is_virtual);
TableMetadataBase(const TableMetadataBase& other)
: MetadataBase(other)
, RefCounted<TableMetadataBase>()
, is_virtual_(other.is_virtual_)
, columns_(other.columns_)
, columns_by_name_(other.columns_by_name_)
, partition_key_(other.partition_key_)
, clustering_key_(other.clustering_key_)
, clustering_key_order_(other.clustering_key_order_) {}
virtual ~TableMetadataBase() {}
bool is_virtual() const { return is_virtual_; }
const ColumnMetadata::Vec& columns() const { return columns_; }
const ColumnMetadata::Vec& partition_key() const { return partition_key_; }
const ColumnMetadata::Vec& clustering_key() const { return clustering_key_; }
const ClusteringOrderVec& clustering_key_order() const { return clustering_key_order_; }
Iterator* iterator_columns() const { return new ColumnIterator(columns_); }
const ColumnMetadata* get_column(const String& name) const;
virtual void add_column(const VersionNumber& server_version, const ColumnMetadata::Ptr& column);
void clear_columns();
void build_keys_and_sort(const VersionNumber& server_version, SimpleDataTypeCache& cache);
protected:
const bool is_virtual_;
ColumnMetadata::Vec columns_;
ColumnMetadata::Map columns_by_name_;
ColumnMetadata::Vec partition_key_;
ColumnMetadata::Vec clustering_key_;
ClusteringOrderVec clustering_key_order_;
};
class ViewMetadata : public TableMetadataBase {
public:
typedef SharedRefPtr<ViewMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
static const ViewMetadata::Ptr NIL;
ViewMetadata(const VersionNumber& server_version, const TableMetadata* table, const String& name,
const RefBuffer::Ptr& buffer, const Row* row, bool is_virtual);
ViewMetadata(const ViewMetadata& other, const TableMetadata* table)
: TableMetadataBase(other)
, base_table_(table) {}
const TableMetadata* base_table() const { return base_table_; }
private:
// This cannot be a reference counted pointer because it would cause a cycle.
// This is okay because the lifetime of the table will exceed the lifetime
// of a table's view. That is, a table's views will be removed when a table is
// removed.
const TableMetadata* base_table_;
};
class ViewIteratorBase : public Iterator {
public:
ViewIteratorBase(CassIteratorType type)
: Iterator(type) {}
virtual ViewMetadata* view() const = 0;
};
class ViewIteratorVec : public ViewIteratorBase {
public:
ViewIteratorVec(const ViewMetadata::Vec& views)
: ViewIteratorBase(CASS_ITERATOR_TYPE_MATERIALIZED_VIEW_META)
, impl_(views) {}
virtual ViewMetadata* view() const { return impl_.item().get(); }
virtual bool next() { return impl_.next(); }
private:
VecIteratorImpl<ViewMetadata::Ptr> impl_;
};
class ViewIteratorMap : public ViewIteratorBase {
public:
ViewIteratorMap(const ViewMetadata::Map& views)
: ViewIteratorBase(CASS_ITERATOR_TYPE_MATERIALIZED_VIEW_META)
, impl_(views) {}
virtual ViewMetadata* view() const { return impl_.item().get(); }
virtual bool next() { return impl_.next(); }
private:
MapIteratorImpl<ViewMetadata::Ptr> impl_;
};
inline bool operator<(const ViewMetadata::Ptr& a, const ViewMetadata::Ptr& b) {
return a->name() < b->name();
}
inline bool operator<(const ViewMetadata::Ptr& a, const String& b) { return a->name() < b; }
inline bool operator==(const ViewMetadata::Ptr& a, const ViewMetadata::Ptr& b) {
return a->name() == b->name();
}
class TableMetadata : public TableMetadataBase {
public:
typedef SharedRefPtr<TableMetadata> Ptr;
typedef internal::Map<String, Ptr> Map;
typedef Vector<Ptr> Vec;
typedef Vector<String> KeyAliases;
static const TableMetadata::Ptr NIL;
class IndexIterator : public MetadataIteratorImpl<VecIteratorImpl<IndexMetadata::Ptr> > {
public:
IndexIterator(const IndexIterator::Collection& collection)
: MetadataIteratorImpl<VecIteratorImpl<IndexMetadata::Ptr> >(CASS_ITERATOR_TYPE_INDEX_META,
collection) {}
const IndexMetadata* index() const { return impl_.item().get(); }
};
TableMetadata(const VersionNumber& server_version, const String& name,
const RefBuffer::Ptr& buffer, const Row* row, bool is_virtual);
TableMetadata(const TableMetadata& other)
: TableMetadataBase(other)
, indexes_(other.indexes_)
, indexes_by_name_(other.indexes_by_name_) {}
const ViewMetadata::Vec& views() const { return views_; }
const IndexMetadata::Vec& indexes() const { return indexes_; }
Iterator* iterator_views() const { return new ViewIteratorVec(views_); }
const ViewMetadata* get_view(const String& name) const;
virtual void add_column(const VersionNumber& server_version, const ColumnMetadata::Ptr& column);
void add_view(const ViewMetadata::Ptr& view);
void sort_views();
Iterator* iterator_indexes() const { return new IndexIterator(indexes_); }
const IndexMetadata* get_index(const String& name) const;
void add_index(const IndexMetadata::Ptr& index);
void clear_indexes();
void key_aliases(SimpleDataTypeCache& cache, KeyAliases* output) const;
private:
ViewMetadata::Vec views_;
IndexMetadata::Vec indexes_;
IndexMetadata::Map indexes_by_name_;
};
class KeyspaceMetadata : public MetadataBase {
public:
typedef internal::Map<String, KeyspaceMetadata> Map;
typedef CopyOnWritePtr<KeyspaceMetadata::Map> MapPtr;
class TableIterator : public MetadataIteratorImpl<MapIteratorImpl<TableMetadata::Ptr> > {
public:
TableIterator(const TableIterator::Collection& collection)
: MetadataIteratorImpl<MapIteratorImpl<TableMetadata::Ptr> >(CASS_ITERATOR_TYPE_TABLE_META,
collection) {}
const TableMetadata* table() const { return static_cast<TableMetadata*>(impl_.item().get()); }
};
class TypeIterator : public MetadataIteratorImpl<MapIteratorImpl<UserType::Ptr> > {
public:
TypeIterator(const TypeIterator::Collection& collection)
: MetadataIteratorImpl<MapIteratorImpl<UserType::Ptr> >(CASS_ITERATOR_TYPE_TYPE_META,
collection) {}
const UserType* type() const { return impl_.item().get(); }
};
class FunctionIterator : public MetadataIteratorImpl<MapIteratorImpl<FunctionMetadata::Ptr> > {
public:
FunctionIterator(const FunctionIterator::Collection& collection)
: MetadataIteratorImpl<MapIteratorImpl<FunctionMetadata::Ptr> >(
CASS_ITERATOR_TYPE_FUNCTION_META, collection) {}
const FunctionMetadata* function() const { return impl_.item().get(); }
};
class AggregateIterator : public MetadataIteratorImpl<MapIteratorImpl<AggregateMetadata::Ptr> > {
public:
AggregateIterator(const AggregateIterator::Collection& collection)
: MetadataIteratorImpl<MapIteratorImpl<AggregateMetadata::Ptr> >(
CASS_ITERATOR_TYPE_AGGREGATE_META, collection) {}
const AggregateMetadata* aggregate() const { return impl_.item().get(); }
};
KeyspaceMetadata(const String& name, bool is_virtual = false)
: MetadataBase(name)
, is_virtual_(is_virtual)
, tables_(new TableMetadata::Map())
, views_(new ViewMetadata::Map())
, user_types_(new UserType::Map())
, functions_(new FunctionMetadata::Map())
, aggregates_(new AggregateMetadata::Map()) {}
void update(const VersionNumber& server_version, const RefBuffer::Ptr& buffer, const Row* row);
bool is_virtual() const { return is_virtual_; }
const FunctionMetadata::Map& functions() const { return *functions_; }
const UserType::Map& user_types() const { return *user_types_; }
Iterator* iterator_tables() const { return new TableIterator(*tables_); }
const TableMetadata* get_table(const String& name) const;
const TableMetadata::Ptr& get_table(const String& name);
void add_table(const TableMetadata::Ptr& table);
Iterator* iterator_views() const { return new ViewIteratorMap(*views_); }
const ViewMetadata* get_view(const String& name) const;
const ViewMetadata::Ptr& get_view(const String& name);
void add_view(const ViewMetadata::Ptr& view);
void drop_table_or_view(const String& table_name);
Iterator* iterator_user_types() const { return new TypeIterator(*user_types_); }
const UserType* get_user_type(const String& type_name) const;
const UserType::Ptr& get_or_create_user_type(const String& name, bool is_frozen);
void drop_user_type(const String& type_name);
Iterator* iterator_functions() const { return new FunctionIterator(*functions_); }
const FunctionMetadata* get_function(const String& full_function_name) const;
void add_function(const FunctionMetadata::Ptr& function);
void drop_function(const String& full_function_name);
Iterator* iterator_aggregates() const { return new AggregateIterator(*aggregates_); }
const AggregateMetadata* get_aggregate(const String& full_aggregate_name) const;
void add_aggregate(const AggregateMetadata::Ptr& aggregate);
void drop_aggregate(const String& full_aggregate_name);
StringRef strategy_class() const { return strategy_class_; }
const Value* strategy_options() const { return &strategy_options_; }
private:
void internal_add_table(const TableMetadata::Ptr& table, const ViewMetadata::Vec& views);
private:
const bool is_virtual_;
StringRef strategy_class_;
Value strategy_options_;
CopyOnWritePtr<TableMetadata::Map> tables_;
CopyOnWritePtr<ViewMetadata::Map> views_;
CopyOnWritePtr<UserType::Map> user_types_;
CopyOnWritePtr<FunctionMetadata::Map> functions_;
CopyOnWritePtr<AggregateMetadata::Map> aggregates_;
};
class Metadata {
public:
class KeyspaceIterator : public MetadataIteratorImpl<MapIteratorImpl<KeyspaceMetadata> > {
public:
KeyspaceIterator(const KeyspaceIterator::Collection& collection)
: MetadataIteratorImpl<MapIteratorImpl<KeyspaceMetadata> >(CASS_ITERATOR_TYPE_KEYSPACE_META,
collection) {}
const KeyspaceMetadata* keyspace() const { return &impl_.item(); }
};
class SchemaSnapshot : public Allocated {
public:
SchemaSnapshot(uint32_t version, const VersionNumber& server_version,
const KeyspaceMetadata::MapPtr& keyspaces)
: version_(version)
, server_version_(server_version)
, keyspaces_(keyspaces) {}
uint32_t version() const { return version_; }
VersionNumber server_version() const { return server_version_; }
const KeyspaceMetadata* get_keyspace(const String& name) const;
Iterator* iterator_keyspaces() const { return new KeyspaceIterator(*keyspaces_); }
const UserType* get_user_type(const String& keyspace_name, const String& type_name) const;
private:
uint32_t version_;
VersionNumber server_version_;
KeyspaceMetadata::MapPtr keyspaces_;
};
static String full_function_name(const String& name, const StringVec& signature);
public:
Metadata()
: updating_(&front_)
, schema_snapshot_version_(0) {
uv_mutex_init(&mutex_);
}
~Metadata() { uv_mutex_destroy(&mutex_); }
SchemaSnapshot schema_snapshot() const;
void update_keyspaces(const ResultResponse* result, bool is_virtual);
void update_tables(const ResultResponse* result);
void update_views(const ResultResponse* result);
void update_columns(const ResultResponse* result);
void update_indexes(const ResultResponse* result);
void update_user_types(const ResultResponse* result);
void update_functions(const ResultResponse* result);
void update_aggregates(const ResultResponse* result);
void drop_keyspace(const String& keyspace_name);
void drop_table_or_view(const String& keyspace_name, const String& table_or_view_name);
void drop_user_type(const String& keyspace_name, const String& type_name);
void drop_function(const String& keyspace_name, const String& full_function_name);
void drop_aggregate(const String& keyspace_name, const String& full_aggregate_name);
// This clears and allows updates to the back buffer while preserving
// the front buffer for snapshots.
void clear_and_update_back(const VersionNumber& server_version);
// This swaps the back buffer to the front and makes incremental updates
// happen directly to the front buffer.
void swap_to_back_and_update_front();
void clear();
private:
bool is_front_buffer() const { return updating_ == &front_; }
private:
class InternalData {
public:
InternalData()
: keyspaces_(new KeyspaceMetadata::Map()) {}
const KeyspaceMetadata::MapPtr& keyspaces() const { return keyspaces_; }
void update_keyspaces(const VersionNumber& server_version, const ResultResponse* result,
bool is_virtual);
void update_tables(const VersionNumber& server_version, const ResultResponse* result);
void update_views(const VersionNumber& server_version, const ResultResponse* result);
void update_columns(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const ResultResponse* result);
void update_legacy_indexes(const VersionNumber& server_version, const ResultResponse* result);
void update_indexes(const VersionNumber& server_version, const ResultResponse* result);
void update_user_types(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const ResultResponse* result);
void update_functions(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const ResultResponse* result);
void update_aggregates(const VersionNumber& server_version, SimpleDataTypeCache& cache,
const ResultResponse* result);
void drop_keyspace(const String& keyspace_name);
void drop_table_or_view(const String& keyspace_name, const String& table_or_view_name);
void drop_user_type(const String& keyspace_name, const String& type_name);
void drop_function(const String& keyspace_name, const String& full_function_name);
void drop_aggregate(const String& keyspace_name, const String& full_aggregate_name);
void clear() { keyspaces_->clear(); }
void swap(InternalData& other) {
CopyOnWritePtr<KeyspaceMetadata::Map> temp = other.keyspaces_;
other.keyspaces_ = keyspaces_;
keyspaces_ = temp;
}
private:
KeyspaceMetadata* get_or_create_keyspace(const String& name, bool is_virtual = false);
private:
CopyOnWritePtr<KeyspaceMetadata::Map> keyspaces_;
private:
DISALLOW_COPY_AND_ASSIGN(InternalData);
};
InternalData* updating_;
InternalData front_;
InternalData back_;
VersionNumber server_version_;
uint32_t schema_snapshot_version_;
// This lock prevents partial snapshots when updating metadata
mutable uv_mutex_t mutex_;
// Only used internally on a single thread, there's
// no need for copy-on-write.
SimpleDataTypeCache cache_;
private:
DISALLOW_COPY_AND_ASSIGN(Metadata);
};
}}} // namespace datastax::internal::core
EXTERNAL_TYPE(datastax::internal::core::Metadata::SchemaSnapshot, CassSchemaMeta)
EXTERNAL_TYPE(datastax::internal::core::KeyspaceMetadata, CassKeyspaceMeta)
EXTERNAL_TYPE(datastax::internal::core::TableMetadata, CassTableMeta)
EXTERNAL_TYPE(datastax::internal::core::ViewMetadata, CassMaterializedViewMeta)
EXTERNAL_TYPE(datastax::internal::core::ColumnMetadata, CassColumnMeta)
EXTERNAL_TYPE(datastax::internal::core::IndexMetadata, CassIndexMeta)
EXTERNAL_TYPE(datastax::internal::core::FunctionMetadata, CassFunctionMeta)
EXTERNAL_TYPE(datastax::internal::core::AggregateMetadata, CassAggregateMeta)
#endif