Merge "Thrift and keyspaces"

From Paweł:

"These series contains some improvements in the way thrift keyspace-related
operations are handled (checks whether set_keyspace was given an existing
keyspace, more informative error message when adding keyspace with
invalid/unsupported placement strategy) and initial implementation of
describe_keyspace and describe_keyspaces."
This commit is contained in:
Avi Kivity
2015-06-02 17:11:55 +03:00
5 changed files with 116 additions and 13 deletions

View File

@@ -55,7 +55,7 @@ public:
compound_type(compound_type&&) = default;
auto const& types() {
auto const& types() const {
return _types;
}

View File

@@ -531,7 +531,8 @@ void database::drop_keyspace(const sstring& name) {
}
void database::add_column_family(const utils::UUID& uuid, column_family&& cf) {
if (_keyspaces.count(cf.schema()->ks_name()) == 0) {
auto ks = _keyspaces.find(cf.schema()->ks_name());
if (ks == _keyspaces.end()) {
throw std::invalid_argument("Keyspace " + cf.schema()->ks_name() + " not defined");
}
if (_column_families.count(uuid) != 0) {
@@ -541,6 +542,7 @@ void database::add_column_family(const utils::UUID& uuid, column_family&& cf) {
if (_ks_cf_to_uuid.count(kscf) != 0) {
throw std::invalid_argument("Column family " + cf.schema()->cf_name() + " exists");
}
ks->second.add_column_family(cf.schema());
_column_families.emplace(uuid, std::move(cf));
_ks_cf_to_uuid.emplace(std::move(kscf), uuid);
}

View File

@@ -187,6 +187,9 @@ public:
const lw_shared_ptr<user_types_metadata>& user_types() const {
return _user_types;
}
void add_column_family(const schema_ptr& s) {
_cf_meta_data.emplace(s->cf_name(), s);
}
};
class keyspace {
@@ -213,6 +216,9 @@ public:
locator::abstract_replication_strategy& get_replication_strategy();
column_family::config make_column_family_config(const schema& s) const;
future<> make_directory_for_column_family(const sstring& name, utils::UUID uuid);
void add_column_family(const schema_ptr& s) {
_metadata->add_column_family(s);
}
private:
sstring column_family_directory(const sstring& name, utils::UUID uuid) const;
};
@@ -271,6 +277,7 @@ public:
bool has_keyspace(const sstring& name) const;
void update_keyspace(const sstring& name);
void drop_keyspace(const sstring& name);
const auto& keyspaces() const { return _keyspaces; }
column_family& find_column_family(const sstring& ks, const sstring& name) throw (no_such_column_family);
const column_family& find_column_family(const sstring& ks, const sstring& name) const throw (no_such_column_family);
column_family& find_column_family(const utils::UUID&) throw (no_such_column_family);

View File

@@ -16,6 +16,7 @@
#include "utils/UUID_gen.hh"
#include <thrift/protocol/TBinaryProtocol.h>
#include <boost/move/iterator.hpp>
#include "utils/class_registrator.hh"
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
@@ -61,6 +62,8 @@ public:
// It's an expected exception, so assume the message
// is fine. Also, we don't want to change its type.
throw;
} catch (no_such_class& nc) {
throw make_exception<InvalidRequestException>("unable to find class '%s'", nc.what());
} catch (std::exception& e) {
// Unexpected exception, wrap it
throw ::apache::thrift::TException(std::string("Internal server error: ") + e.what());
@@ -111,12 +114,12 @@ public:
}
void set_keyspace(tcxx::function<void()> cob, tcxx::function<void(::apache::thrift::TDelayedException* _throw)> exn_cob, const std::string& keyspace) {
try {
if (!_db.local().has_keyspace(keyspace)) {
complete_with_exception<InvalidRequestException>(std::move(exn_cob),
"keyspace %s does not exist", keyspace);
} else {
_ks_name = keyspace;
cob();
} catch (std::out_of_range& e) {
return complete_with_exception<InvalidRequestException>(std::move(exn_cob),
"keyspace %s does not exist", keyspace);
}
}
@@ -349,9 +352,11 @@ public:
}
void describe_keyspaces(tcxx::function<void(std::vector<KsDef> const& _return)> cob, tcxx::function<void(::apache::thrift::TDelayedException* _throw)> exn_cob) {
std::vector<KsDef> _return;
// FIXME: implement
return pass_unimplemented(exn_cob);
std::vector<KsDef> ret;
for (auto&& ks : _db.local().keyspaces()) {
ret.emplace_back(get_keyspace_definition(ks.second));
}
cob(ret);
}
void describe_cluster_name(tcxx::function<void(std::string const& _return)> cob) {
@@ -397,9 +402,15 @@ public:
}
void describe_keyspace(tcxx::function<void(KsDef const& _return)> cob, tcxx::function<void(::apache::thrift::TDelayedException* _throw)> exn_cob, const std::string& keyspace) {
KsDef _return;
// FIXME: implement
return pass_unimplemented(exn_cob);
try {
auto& ks = _db.local().find_keyspace(keyspace);
KsDef ret = get_keyspace_definition(ks);
cob(ret);
}
catch (no_such_keyspace& nsk) {
complete_with_exception<InvalidRequestException>(std::move(exn_cob),
"keyspace %s does not exist", keyspace);
}
}
void describe_splits(tcxx::function<void(std::vector<std::string> const& _return)> cob, tcxx::function<void(::apache::thrift::TDelayedException* _throw)> exn_cob, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) {
@@ -556,6 +567,80 @@ public:
}
private:
static sstring class_from_data_type(const data_type& dt) {
static const std::unordered_map<sstring, sstring> types = {
{ "boolean", "BooleanType" },
{ "bytes", "BytesType" },
{ "double", "DoubleType" },
{ "int32", "Int32Type" },
{ "long", "LongType" },
{ "timestamp", "DateType" },
{ "timeuuid", "TimeUUIDType" },
{ "utf8", "UTF8Type" },
{ "uuid", "UUIDType" },
// FIXME: missing types
};
auto it = types.find(dt->name());
if (it == types.end()) {
return sstring("<unknown> ") + dt->name();
}
return sstring("org.apache.cassandra.db.marshal.") + it->second;
}
static sstring class_from_compound_type(const compound_type<allow_prefixes::no>& ct) {
if (ct.is_singular()) {
return class_from_data_type(ct.types().front());
}
sstring type = "org.apache.cassandra.db.marshal.CompositeType(";
for (auto& dt : ct.types()) {
type += class_from_data_type(dt);
if (&dt != &*ct.types().rbegin()) {
type += ",";
}
}
type += ")";
return type;
}
static KsDef get_keyspace_definition(const keyspace& ks) {
auto&& meta = ks.metadata();
KsDef def;
def.__set_name(meta->name());
def.__set_strategy_class(meta->strategy_name());
std::map<std::string, std::string> options(
meta->strategy_options().begin(),
meta->strategy_options().end());
def.__set_strategy_options(options);
std::vector<CfDef> cfs;
for (auto&& cf : meta->cf_meta_data()) {
// FIXME: skip cql3 column families
auto&& s = cf.second;
CfDef cf_def;
cf_def.__set_keyspace(s->ks_name());
cf_def.__set_name(s->cf_name());
cf_def.__set_key_validation_class(class_from_compound_type(*s->partition_key_type()));
if (s->clustering_key_size()) {
cf_def.__set_comparator_type(class_from_compound_type(*s->clustering_key_type()));
} else {
cf_def.__set_comparator_type(class_from_data_type(s->regular_column_name_type()));
}
cf_def.__set_comment(s->comment());
cf_def.__set_bloom_filter_fp_chance(s->bloom_filter_fp_chance());
if (s->regular_columns_count()) {
std::vector<ColumnDef> columns;
for (auto&& c : s->regular_columns()) {
ColumnDef c_def;
c_def.__set_name(c.name_as_text());
c_def.__set_validation_class(class_from_data_type(c.type));
columns.emplace_back(std::move(c_def));
}
cf_def.__set_column_metadata(columns);
}
// FIXME: there are more fields that should be filled...
cfs.emplace_back(std::move(cf_def));
}
def.__set_cf_defs(cfs);
def.__set_durable_writes(meta->durable_writes());
return std::move(def);
}
static column_family& lookup_column_family(database& db, const sstring& ks_name, const sstring& cf_name) {
try {
return db.find_column_family(ks_name, cf_name);

View File

@@ -4,6 +4,11 @@
#pragma once
class no_such_class : public std::runtime_error {
public:
using runtime_error::runtime_error;
};
// BaseType is a base type of a type hierarchy that this registry will hold
// Args... are parameters for object's constructor
template<typename BaseType, typename... Args>
@@ -45,7 +50,11 @@ struct class_registrator {
template<typename BaseType, typename... Args>
std::unique_ptr<BaseType> class_registry<BaseType, Args...>::create(const sstring& name, Args... args) {
return _classes[name](args...);
auto it = _classes.find(name);
if (it == _classes.end()) {
throw no_such_class(name);
}
return it->second(args...);
}
template<typename BaseType, typename... Args>