/*
* Copyright (C) 2014 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see .
*/
// Some thrift headers include other files from within namespaces,
// which is totally broken. Include those files here to avoid
// breakage:
#include
// end thrift workaround
#include "Cassandra.h"
#include "core/distributed.hh"
#include "database.hh"
#include "core/sstring.hh"
#include "core/print.hh"
#include "frozen_mutation.hh"
#include "utils/UUID_gen.hh"
#include
#include
#include "db/marshal/type_parser.hh"
#include "service/migration_manager.hh"
#include "utils/class_registrator.hh"
#include "noexcept_traits.hh"
#include "schema_registry.hh"
#include "thrift/utils.hh"
#include "schema_builder.hh"
#include "thrift/thrift_validation.hh"
#include "service/storage_service.hh"
#include "service/query_state.hh"
#include "cql3/query_processor.hh"
#include
#include
#include
#include
#include
#include
#include "query-result-reader.hh"
#include "thrift/server.hh"
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::async;
using namespace ::org::apache::cassandra;
using namespace thrift;
class unimplemented_exception : public std::exception {
public:
virtual const char* what() const throw () override { return "sorry, not implemented"; }
};
void pass_unimplemented(const tcxx::function& exn_cob) {
exn_cob(::apache::thrift::TDelayedException::delayException(unimplemented_exception()));
}
class delayed_exception_wrapper : public ::apache::thrift::TDelayedException {
std::exception_ptr _ex;
public:
delayed_exception_wrapper(std::exception_ptr ex) : _ex(std::move(ex)) {}
virtual void throw_it() override {
// Thrift auto-wraps unexpected exceptions (those not derived from TException)
// with a TException, but with a fairly bad what(). So detect this, and
// provide our own TException with a better what().
try {
std::rethrow_exception(std::move(_ex));
} catch (const ::apache::thrift::TException&) {
// 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(nc.what());
} catch (marshal_exception& me) {
throw make_exception(me.what());
} catch (exceptions::already_exists_exception& ae) {
throw make_exception(ae.what());
} catch (exceptions::configuration_exception& ce) {
throw make_exception(ce.what());
} catch (exceptions::invalid_request_exception& ire) {
throw make_exception(ire.what());
} catch (no_such_column_family& nocf) {
throw make_exception(nocf.what());
} catch (no_such_keyspace&) {
throw NotFoundException();
} catch (exceptions::syntax_exception& se) {
throw make_exception("syntax error: %s", se.what());
} catch (exceptions::authentication_exception& ae) {
throw make_exception(ae.what());
} catch (exceptions::unauthorized_exception& ue) {
throw make_exception(ue.what());
} catch (std::exception& e) {
// Unexpected exception, wrap it
throw ::apache::thrift::TException(std::string("Internal server error: ") + e.what());
} catch (...) {
// Unexpected exception, wrap it, unfortunately without any info
throw ::apache::thrift::TException("Internal server error");
}
}
};
template
void
with_cob(tcxx::function&& cob,
tcxx::function&& exn_cob,
Func&& func) {
// then_wrapped() terminates the fiber by calling one of the cob objects
futurize>::apply([func = std::forward(func)] {
return noexcept_movable::wrap(func());
}).then_wrapped([cob = std::move(cob), exn_cob = std::move(exn_cob)] (auto&& f) {
try {
cob(noexcept_movable::unwrap(f.get0()));
} catch (...) {
delayed_exception_wrapper dew(std::current_exception());
exn_cob(&dew);
}
});
}
template
void
with_cob(tcxx::function&& cob,
tcxx::function&& exn_cob,
Func&& func) {
// then_wrapped() terminates the fiber by calling one of the cob objects
futurize::apply(func).then_wrapped([cob = std::move(cob), exn_cob = std::move(exn_cob)] (future<> f) {
try {
f.get();
cob();
} catch (...) {
delayed_exception_wrapper dew(std::current_exception());
exn_cob(&dew);
}
});
}
template
void
with_exn_cob(tcxx::function&& exn_cob, Func&& func) {
// then_wrapped() terminates the fiber by calling one of the cob objects
futurize::apply(func).then_wrapped([exn_cob = std::move(exn_cob)] (future<> f) {
try {
f.get();
} catch (...) {
delayed_exception_wrapper dew(std::current_exception());
exn_cob(&dew);
}
});
}
std::string bytes_to_string(bytes_view v) {
return { reinterpret_cast(v.begin()), v.size() };
}
namespace thrift {
GCC6_CONCEPT(
template
concept bool Aggregator =
requires() { typename T::type; }
&& requires(T aggregator, typename T::type* aggregation, const bytes& name, const query::result_atomic_cell_view& cell) {
{ aggregator.on_column(aggregation, name, cell) } -> void;
};
)
}
class thrift_handler : public CassandraCobSvIf {
distributed& _db;
distributed& _query_processor;
service::query_state _query_state;
private:
template
void
with_schema(Cob&& cob,
tcxx::function&& exn_cob,
const std::string& cf,
Func&& func) {
with_cob(std::move(cob), std::move(exn_cob), [this, &cf, func = std::move(func)] {
auto schema = lookup_schema(_db.local(), current_keyspace(), cf);
return func(std::move(schema));
});
}
public:
explicit thrift_handler(distributed& db, distributed& qp)
: _db(db)
, _query_processor(qp)
, _query_state(service::client_state::for_external_thrift_calls())
{ }
const sstring& current_keyspace() const {
return _query_state.get_client_state().get_raw_keyspace();
}
void validate_login() const {
return _query_state.get_client_state().validate_login();
};
void login(tcxx::function cob, tcxx::function exn_cob, const AuthenticationRequest& auth_request) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auth::authenticator::credentials_map creds(auth_request.credentials.begin(), auth_request.credentials.end());
return auth::authenticator::get().authenticate(creds).then([this] (auto user) {
_query_state.get_client_state().set_login(std::move(user));
});
});
}
void set_keyspace(tcxx::function cob, tcxx::function exn_cob, const std::string& keyspace) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
_query_state.get_client_state().set_keyspace(_db, keyspace);
});
}
void get(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) {
return get_slice([cob = std::move(cob), &column_path](auto&& results) {
if (results.empty()) {
throw NotFoundException();
}
return cob(std::move(results.front()));
}, exn_cob, key, column_path_to_column_parent(column_path), column_path_to_slice_predicate(column_path), std::move(consistency_level));
}
void get_slice(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) {
return multiget_slice([cob = std::move(cob)](auto&& results) {
if (!results.empty()) {
return cob(std::move(results.begin()->second));
}
return cob({ });
}, exn_cob, {key}, column_parent, predicate, consistency_level);
}
void get_count(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) {
return multiget_count([cob = std::move(cob)](auto&& results) {
if (!results.empty()) {
return cob(results.begin()->second);
}
return cob(0);
}, exn_cob, {key}, column_parent, predicate, consistency_level);
}
void multiget_slice(tcxx::function > const& _return)> cob, tcxx::function exn_cob, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_parent.column_family, [&](schema_ptr schema) {
if (!column_parent.super_column.empty()) {
fail(unimplemented::cause::SUPER);
}
auto cmd = slice_pred_to_read_cmd(*schema, predicate);
auto cell_limit = predicate.__isset.slice_range ? static_cast(predicate.slice_range.count) : std::numeric_limits::max();
auto pranges = make_partition_ranges(*schema, keys);
auto f = _query_state.get_client_state().has_schema_access(*schema, auth::permission::SELECT);
return f.then([schema, cmd, pranges = std::move(pranges), cell_limit, consistency_level]() mutable {
return service::get_local_storage_proxy().query(
schema,
cmd,
std::move(pranges),
cl_from_thrift(consistency_level),
nullptr).then([schema, cmd, cell_limit](auto result) {
return query::result_view::do_with(*result, [schema, cmd, cell_limit](query::result_view v) {
if (schema->is_counter()) {
counter_column_aggregator aggregator(*schema, cmd->slice, cell_limit);
v.consume(cmd->slice, aggregator);
return aggregator.release_as_map();
}
column_aggregator aggregator(*schema, cmd->slice, cell_limit);
v.consume(cmd->slice, aggregator);
return aggregator.release_as_map();
});
});
});
});
}
void multiget_count(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::vector & keys, const ColumnParent& column_parent, const SlicePredicate& predicate, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_parent.column_family, [&](schema_ptr schema) {
if (!column_parent.super_column.empty()) {
fail(unimplemented::cause::SUPER);
}
auto cmd = slice_pred_to_read_cmd(*schema, predicate);
auto cell_limit = predicate.__isset.slice_range ? static_cast(predicate.slice_range.count) : std::numeric_limits::max();
auto pranges = make_partition_ranges(*schema, keys);
auto f = _query_state.get_client_state().has_schema_access(*schema, auth::permission::SELECT);
return f.then([schema, cmd, pranges = std::move(pranges), cell_limit, consistency_level]() mutable {
return service::get_local_storage_proxy().query(
schema,
cmd,
std::move(pranges),
cl_from_thrift(consistency_level),
nullptr).then([schema, cmd, cell_limit](auto&& result) {
return query::result_view::do_with(*result, [schema, cmd, cell_limit](query::result_view v) {
column_counter counter(*schema, cmd->slice, cell_limit);
v.consume(cmd->slice, counter);
return counter.release_as_map();
});
});
});
});
}
/**
* In origin, empty partitions are returned as part of the KeySlice, for which the key will be filled
* in but the columns vector will be empty. Since in our case we don't return empty partitions, we
* don't know which partition keys in the specified range we should return back to the client. So for
* now our behavior differs from Origin.
*/
void get_range_slices(tcxx::function const& _return)> cob, tcxx::function exn_cob, const ColumnParent& column_parent, const SlicePredicate& predicate, const KeyRange& range, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_parent.column_family, [&](schema_ptr schema) {
if (!column_parent.super_column.empty()) {
fail(unimplemented::cause::SUPER);
}
auto&& prange = make_partition_range(*schema, range);
auto cmd = slice_pred_to_read_cmd(*schema, predicate);
// KeyRange::count is the number of thrift rows to return, while
// SlicePredicte::slice_range::count limits the number of thrift colums.
if (schema->thrift().is_dynamic()) {
// For dynamic CFs we must limit the number of partitions returned.
cmd->partition_limit = range.count;
} else {
// For static CFs each thrift row maps to a CQL row.
cmd->row_limit = range.count;
}
auto f = _query_state.get_client_state().has_schema_access(*schema, auth::permission::SELECT);
return f.then([schema, cmd, prange = std::move(prange), consistency_level] () mutable {
return service::get_local_storage_proxy().query(
schema,
cmd,
std::move(prange),
cl_from_thrift(consistency_level),
nullptr).then([schema, cmd](auto result) {
return query::result_view::do_with(*result, [schema, cmd](query::result_view v) {
return to_key_slices(*schema, cmd->slice, v, std::numeric_limits::max());
});
});
});
});
}
static lw_shared_ptr make_paged_read_cmd(const schema& s, uint32_t column_limit, const std::string* start_column, const dht::partition_range_vector& range) {
auto opts = query_opts(s);
std::vector clustering_ranges;
std::vector regular_columns;
uint32_t row_limit;
uint32_t partition_limit;
std::unique_ptr specific_ranges = nullptr;
// KeyRange::count is the number of thrift columns to return (unlike get_range_slices).
if (s.thrift().is_dynamic()) {
// For dynamic CFs we must limit the number of rows returned. We use the query::specific_ranges to constrain
// the first partition, of which we are only interested in the columns after start_column.
row_limit = column_limit;
partition_limit = query::max_partitions;
if (start_column) {
auto sr = query::specific_ranges(*range[0].start()->value().key(), {make_clustering_range_and_validate(s, *start_column, std::string())});
specific_ranges = std::make_unique(std::move(sr));
}
regular_columns.emplace_back(s.regular_begin()->id);
} else {
// For static CFs we must limit the number of columns returned. Since we don't implement a cell limit,
// we ask for as many partitions as those that are capable of exhausting the limit and later filter out
// any excess cells.
row_limit = query::max_rows;
partition_limit = (column_limit + s.regular_columns_count() - 1) / s.regular_columns_count();
schema::const_iterator start_col = start_column
? s.regular_lower_bound(to_bytes(*start_column))
: s.regular_begin();
regular_columns = add_columns(start_col, s.regular_end(), false);
}
clustering_ranges.emplace_back(query::clustering_range::make_open_ended_both_sides());
auto slice = query::partition_slice(std::move(clustering_ranges), { }, std::move(regular_columns), opts,
std::move(specific_ranges), cql_serialization_format::internal());
return make_lw_shared(s.id(), s.version(), std::move(slice), row_limit, gc_clock::now(), stdx::nullopt, partition_limit);
}
static future<> do_get_paged_slice(
schema_ptr schema,
uint32_t column_limit,
dht::partition_range_vector range,
const std::string* start_column,
db::consistency_level consistency_level,
std::vector& output) {
auto cmd = make_paged_read_cmd(*schema, column_limit, start_column, range);
stdx::optional start_key;
auto end = range[0].end();
if (start_column && !schema->thrift().is_dynamic()) {
// For static CFs, we must first query for a specific key so as to consume the remainder
// of columns in that partition.
start_key = range[0].start()->value().key();
range = {dht::partition_range::make_singular(std::move(range[0].start()->value()))};
}
auto range1 = range; // query() below accepts an rvalue, so need a copy to reuse later
return service::get_local_storage_proxy().query(schema, cmd, std::move(range), consistency_level, nullptr).then([schema, cmd, column_limit](auto result) {
return query::result_view::do_with(*result, [schema, cmd, column_limit](query::result_view v) {
return to_key_slices(*schema, cmd->slice, v, column_limit);
});
}).then([schema, cmd, column_limit, range = std::move(range1), consistency_level, start_key = std::move(start_key), end = std::move(end), &output](auto&& slices) mutable {
auto columns = std::accumulate(slices.begin(), slices.end(), 0u, [](auto&& acc, auto&& ks) {
return acc + ks.columns.size();
});
std::move(slices.begin(), slices.end(), std::back_inserter(output));
if (columns == 0 || columns == column_limit || (slices.size() < cmd->partition_limit && columns < cmd->row_limit)) {
if (!output.empty() || !start_key) {
if (range.size() > 1 && columns < column_limit) {
range.erase(range.begin());
return do_get_paged_slice(std::move(schema), column_limit - columns, std::move(range), nullptr, consistency_level, output);
}
return make_ready_future();
}
// The single, first partition we queried was empty, so retry with no start column.
} else {
start_key = key_from_thrift(*schema, to_bytes_view(output.back().key));
}
auto start = dht::global_partitioner().decorate_key(*schema, std::move(*start_key));
range[0] = dht::partition_range(dht::partition_range::bound(std::move(start), false), std::move(end));
return do_get_paged_slice(schema, column_limit - columns, std::move(range), nullptr, consistency_level, output);
});
}
void get_paged_slice(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& column_family, const KeyRange& range, const std::string& start_column, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_family, [&](schema_ptr schema) {
return do_with(std::vector(), [&](auto& output) {
if (range.__isset.row_filter) {
throw make_exception("Cross-row paging is not supported along with index clauses");
}
if (range.count <= 0) {
throw make_exception("Count must be positive");
}
auto&& prange = make_partition_range(*schema, range);
if (!start_column.empty()) {
auto&& start_bound = prange[0].start();
if (!(start_bound && start_bound->is_inclusive() && start_bound->value().has_key())) {
// According to Orign's DataRange#Paging#slicesForKey.
throw make_exception("If start column is provided, so must the start key");
}
}
auto f = _query_state.get_client_state().has_schema_access(*schema, auth::permission::SELECT);
return f.then([schema, count = range.count, start_column, prange = std::move(prange), consistency_level, &output] () mutable {
return do_get_paged_slice(std::move(schema), count, std::move(prange), &start_column,
cl_from_thrift(consistency_level), output).then([&output] {
return std::move(output);
});
});
});
});
}
void get_indexed_slices(tcxx::function const& _return)> cob, tcxx::function exn_cob, const ColumnParent& column_parent, const IndexClause& index_clause, const SlicePredicate& column_predicate, const ConsistencyLevel::type consistency_level) {
std::vector _return;
warn(unimplemented::cause::INDEXES);
// FIXME: implement
return pass_unimplemented(exn_cob);
}
void insert(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnParent& column_parent, const Column& column, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_parent.column_family, [&](schema_ptr schema) {
if (column_parent.__isset.super_column) {
fail(unimplemented::cause::SUPER);
}
if (schema->is_view()) {
throw make_exception("Cannot modify Materialized Views directly");
}
mutation m_to_apply(key_from_thrift(*schema, to_bytes_view(key)), schema);
add_to_mutation(*schema, column, m_to_apply);
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY).then([m_to_apply = std::move(m_to_apply), consistency_level] {
return service::get_local_storage_proxy().mutate({std::move(m_to_apply)}, cl_from_thrift(consistency_level), nullptr);
});
});
}
void add(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnParent& column_parent, const CounterColumn& column, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_parent.column_family, [&](schema_ptr schema) {
if (column_parent.__isset.super_column) {
fail(unimplemented::cause::SUPER);
}
mutation m_to_apply(key_from_thrift(*schema, to_bytes_view(key)), schema);
add_to_mutation(*schema, column, m_to_apply);
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY).then([m_to_apply = std::move(m_to_apply), consistency_level] {
return service::get_local_storage_proxy().mutate({std::move(m_to_apply)}, cl_from_thrift(consistency_level), nullptr);
});
});
}
void cas(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const std::string& column_family, const std::vector & expected, const std::vector & updates, const ConsistencyLevel::type serial_consistency_level, const ConsistencyLevel::type commit_consistency_level) {
CASResult _return;
warn(unimplemented::cause::LWT);
// FIXME: implement
return pass_unimplemented(exn_cob);
}
void remove(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnPath& column_path, const int64_t timestamp, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_path.column_family, [&](schema_ptr schema) {
if (schema->is_view()) {
throw make_exception("Cannot modify Materialized Views directly");
}
mutation m_to_apply(key_from_thrift(*schema, to_bytes_view(key)), schema);
if (column_path.__isset.super_column) {
fail(unimplemented::cause::SUPER);
} else if (column_path.__isset.column) {
Deletion d;
d.__set_timestamp(timestamp);
d.__set_predicate(column_path_to_slice_predicate(column_path));
Mutation m;
m.__set_deletion(d);
add_to_mutation(*schema, m, m_to_apply);
} else {
m_to_apply.partition().apply(tombstone(timestamp, gc_clock::now()));
}
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY).then([m_to_apply = std::move(m_to_apply), consistency_level] {
return service::get_local_storage_proxy().mutate({std::move(m_to_apply)}, cl_from_thrift(consistency_level), nullptr);
});
});
}
void remove_counter(tcxx::function cob, tcxx::function exn_cob, const std::string& key, const ColumnPath& column_path, const ConsistencyLevel::type consistency_level) {
with_schema(std::move(cob), std::move(exn_cob), column_path.column_family, [&](schema_ptr schema) {
mutation m_to_apply(key_from_thrift(*schema, to_bytes_view(key)), schema);
auto timestamp = api::new_timestamp();
if (column_path.__isset.super_column) {
fail(unimplemented::cause::SUPER);
} else if (column_path.__isset.column) {
Deletion d;
d.__set_timestamp(timestamp);
d.__set_predicate(column_path_to_slice_predicate(column_path));
Mutation m;
m.__set_deletion(d);
add_to_mutation(*schema, m, m_to_apply);
} else {
m_to_apply.partition().apply(tombstone(timestamp, gc_clock::now()));
}
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY).then([m_to_apply = std::move(m_to_apply), consistency_level] {
// This mutation contains only counter tombstones so it can be applied like non-counter mutations.
return service::get_local_storage_proxy().mutate({std::move(m_to_apply)}, cl_from_thrift(consistency_level), nullptr);
});
});
}
void batch_mutate(tcxx::function cob, tcxx::function exn_cob, const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auto p = prepare_mutations(_db.local(), current_keyspace(), mutation_map);
return parallel_for_each(std::move(p.second), [this](auto&& schema) {
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY);
}).then([muts = std::move(p.first), consistency_level] {
return service::get_local_storage_proxy().mutate(std::move(muts), cl_from_thrift(consistency_level), nullptr);
});
});
}
void atomic_batch_mutate(tcxx::function cob, tcxx::function exn_cob, const std::map > > & mutation_map, const ConsistencyLevel::type consistency_level) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auto p = prepare_mutations(_db.local(), current_keyspace(), mutation_map);
return parallel_for_each(std::move(p.second), [this](auto&& schema) {
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::MODIFY);
}).then([muts = std::move(p.first), consistency_level] {
return service::get_local_storage_proxy().mutate_atomically(std::move(muts), cl_from_thrift(consistency_level), nullptr);
});
});
}
void truncate(tcxx::function cob, tcxx::function exn_cob, const std::string& cfname) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
if (current_keyspace().empty()) {
throw make_exception("keyspace not set");
}
return _query_state.get_client_state().has_column_family_access(current_keyspace(), cfname, auth::permission::MODIFY).then([=] {
if (_db.local().find_schema(current_keyspace(), cfname)->is_view()) {
throw make_exception("Cannot truncate Materialized Views");
}
return service::get_local_storage_proxy().truncate_blocking(current_keyspace(), cfname);
});
});
}
void get_multi_slice(tcxx::function const& _return)> cob, tcxx::function exn_cob, const MultiSliceRequest& request) {
with_schema(std::move(cob), std::move(exn_cob), request.column_parent.column_family, [&](schema_ptr schema) {
if (!request.__isset.key) {
throw make_exception("Key may not be empty");
}
if (!request.__isset.column_parent || request.column_parent.column_family.empty()) {
throw make_exception("non-empty table is required");
}
if (!request.column_parent.super_column.empty()) {
throw make_exception("get_multi_slice does not support super columns");
}
auto& s = *schema;
auto pk = key_from_thrift(s, to_bytes(request.key));
auto dk = dht::global_partitioner().decorate_key(s, pk);
std::vector regular_columns;
std::vector clustering_ranges;
auto opts = query_opts(s);
uint32_t row_limit;
if (s.thrift().is_dynamic()) {
row_limit = request.count;
auto cmp = bound_view::compare(s);
clustering_ranges = make_non_overlapping_ranges(std::move(request.column_slices), [&s](auto&& cslice) {
return make_clustering_range(s, cslice.start, cslice.finish);
}, clustering_key_prefix::prefix_equal_tri_compare(s), [cmp = std::move(cmp)](auto& range) {
auto bounds = bound_view::from_range(range);
return cmp(bounds.second, bounds.first);
}, request.reversed);
regular_columns.emplace_back(s.regular_begin()->id);
if (request.reversed) {
opts.set(query::partition_slice::option::reversed);
}
} else {
row_limit = query::max_rows;
clustering_ranges.emplace_back(query::clustering_range::make_open_ended_both_sides());
auto cmp = [&s](auto&& s1, auto&& s2) { return s.regular_column_name_type()->compare(s1, s2); };
auto ranges = make_non_overlapping_ranges(std::move(request.column_slices), [](auto&& cslice) {
return make_range(cslice.start, cslice.finish);
}, cmp, [&](auto& range) { return range.is_wrap_around(cmp); }, request.reversed);
auto on_range = [&](auto&& range) {
auto start = range.start() ? s.regular_lower_bound(range.start()->value()) : s.regular_begin();
auto end = range.end() ? s.regular_upper_bound(range.end()->value()) : s.regular_end();
regular_columns = add_columns(start, end, request.reversed);
};
if (request.reversed) {
std::for_each(ranges.rbegin(), ranges.rend(), on_range);
} else {
std::for_each(ranges.begin(), ranges.end(), on_range);
}
}
auto slice = query::partition_slice(std::move(clustering_ranges), {}, std::move(regular_columns), opts, nullptr);
auto cmd = make_lw_shared(schema->id(), schema->version(), std::move(slice), row_limit);
auto f = _query_state.get_client_state().has_schema_access(*schema, auth::permission::SELECT);
return f.then([dk = std::move(dk), cmd, schema, column_limit = request.count, cl = request.consistency_level] {
return service::get_local_storage_proxy().query(
schema,
cmd,
{dht::partition_range::make_singular(dk)},
cl_from_thrift(cl),
nullptr).then([schema, cmd, column_limit](auto result) {
return query::result_view::do_with(*result, [schema, cmd, column_limit](query::result_view v) {
column_aggregator aggregator(*schema, cmd->slice, column_limit);
v.consume(cmd->slice, aggregator);
auto cols = aggregator.release();
return !cols.empty() ? std::move(cols.begin()->second) : std::vector();
});
});
});
});
}
void describe_schema_versions(tcxx::function > const& _return)> cob, tcxx::function exn_cob) {
with_cob(std::move(cob), std::move(exn_cob), [] {
return service::get_local_storage_service().describe_schema_versions().then([](auto&& m) {
std::map> ret;
for (auto&& p : m) {
ret[p.first] = std::vector(p.second.begin(), p.second.end());
}
return ret;
});
});
}
void describe_keyspaces(tcxx::function const& _return)> cob, tcxx::function exn_cob) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
validate_login();
std::vector ret;
for (auto&& ks : _db.local().keyspaces()) {
ret.emplace_back(get_keyspace_definition(ks.second));
}
return ret;
});
}
void describe_cluster_name(tcxx::function cob) {
cob(_db.local().get_config().cluster_name());
}
void describe_version(tcxx::function cob) {
cob(org::apache::cassandra::thrift_version);
}
void do_describe_ring(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& keyspace, bool local) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auto& ks = _db.local().find_keyspace(keyspace);
if (ks.get_replication_strategy().get_type() == locator::replication_strategy_type::local) {
throw make_exception("There is no ring for the keyspace: %s", keyspace);
}
auto ring = service::get_local_storage_service().describe_ring(keyspace, local);
std::vector ret;
ret.reserve(ring.size());
std::transform(ring.begin(), ring.end(), std::back_inserter(ret), [](auto&& tr) {
TokenRange token_range;
token_range.__set_start_token(std::move(tr._start_token));
token_range.__set_end_token(std::move(tr._end_token));
token_range.__set_endpoints(std::vector(tr._endpoints.begin(), tr._endpoints.end()));
std::vector eds;
std::transform(tr._endpoint_details.begin(), tr._endpoint_details.end(), std::back_inserter(eds), [](auto&& ed) {
EndpointDetails detail;
detail.__set_host(ed._host);
detail.__set_datacenter(ed._datacenter);
detail.__set_rack(ed._rack);
return detail;
});
token_range.__set_endpoint_details(std::move(eds));
token_range.__set_rpc_endpoints(std::vector(tr._rpc_endpoints.begin(), tr._rpc_endpoints.end()));
return token_range;
});
return ret;
});
}
void describe_ring(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& keyspace) {
do_describe_ring(std::move(cob), std::move(exn_cob), keyspace, false);
}
void describe_local_ring(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& keyspace) {
do_describe_ring(std::move(cob), std::move(exn_cob), keyspace, true);
}
void describe_token_map(tcxx::function const& _return)> cob, tcxx::function exn_cob) {
with_cob(std::move(cob), std::move(exn_cob), [] {
auto m = service::get_local_storage_service().get_token_to_endpoint_map();
std::map ret;
for (auto&& p : m) {
ret[sprint("%s", p.first)] = p.second.to_sstring();
}
return ret;
});
}
void describe_partitioner(tcxx::function cob) {
cob(dht::global_partitioner().name());
}
void describe_snitch(tcxx::function cob) {
cob(sprint("org.apache.cassandra.locator.%s", _db.local().get_snitch_name()));
}
void describe_keyspace(tcxx::function cob, tcxx::function exn_cob, const std::string& keyspace) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
validate_login();
auto& ks = _db.local().find_keyspace(keyspace);
return get_keyspace_definition(ks);
});
}
void describe_splits(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) {
return describe_splits_ex([cob = std::move(cob)](auto&& results) {
std::vector res;
res.reserve(results.size() + 1);
res.emplace_back(results[0].start_token);
for (auto&& s : results) {
res.emplace_back(std::move(s.end_token));
}
return cob(std::move(res));
}, exn_cob, cfName, start_token, end_token, keys_per_split);
}
void trace_next_query(tcxx::function cob) {
std::string _return;
// FIXME: implement
return cob("dummy trace");
}
void describe_splits_ex(tcxx::function const& _return)> cob, tcxx::function exn_cob, const std::string& cfName, const std::string& start_token, const std::string& end_token, const int32_t keys_per_split) {
with_cob(std::move(cob), std::move(exn_cob), [&]{
dht::token_range_vector ranges;
auto tstart = start_token.empty() ? dht::minimum_token() : dht::global_partitioner().from_sstring(sstring(start_token));
auto tend = end_token.empty() ? dht::maximum_token() : dht::global_partitioner().from_sstring(sstring(end_token));
range r({{ std::move(tstart), false }}, {{ std::move(tend), true }});
auto cf = sstring(cfName);
auto splits = service::get_local_storage_service().get_splits(current_keyspace(), cf, std::move(r), keys_per_split);
std::vector res;
for (auto&& s : splits) {
res.emplace_back();
assert(s.first.start() && s.first.end());
auto start_token = dht::global_partitioner().to_sstring(s.first.start()->value());
auto end_token = dht::global_partitioner().to_sstring(s.first.end()->value());
res.back().__set_start_token(bytes_to_string(to_bytes_view(start_token)));
res.back().__set_end_token(bytes_to_string(to_bytes_view(end_token)));
res.back().__set_row_count(s.second);
}
return res;
});
}
void system_add_column_family(tcxx::function cob, tcxx::function exn_cob, const CfDef& cf_def) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
if (!_db.local().has_keyspace(cf_def.keyspace)) {
throw NotFoundException();
}
if (_db.local().has_schema(cf_def.keyspace, cf_def.name)) {
throw make_exception("Column family %s already exists", cf_def.name);
}
auto s = schema_from_thrift(cf_def, cf_def.keyspace);
return _query_state.get_client_state().has_keyspace_access(cf_def.keyspace, auth::permission::CREATE).then([this, s = std::move(s)] {
return service::get_local_migration_manager().announce_new_column_family(std::move(s), false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void system_drop_column_family(tcxx::function cob, tcxx::function exn_cob, const std::string& column_family) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
return _query_state.get_client_state().has_column_family_access(current_keyspace(), column_family, auth::permission::DROP).then([=] {
auto& cf = _db.local().find_column_family(current_keyspace(), column_family);
if (cf.schema()->is_view()) {
throw make_exception("Cannot drop Materialized Views from Thrift");
}
if (!cf.views().empty()) {
throw make_exception("Cannot drop table with Materialized Views %s", column_family);
}
return service::get_local_migration_manager().announce_column_family_drop(current_keyspace(), column_family, false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void system_add_keyspace(tcxx::function cob, tcxx::function exn_cob, const KsDef& ks_def) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auto ksm = keyspace_from_thrift(ks_def);
return _query_state.get_client_state().has_all_keyspaces_access(auth::permission::CREATE).then([this, ksm = std::move(ksm)] {
return service::get_local_migration_manager().announce_new_keyspace(std::move(ksm), false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void system_drop_keyspace(tcxx::function cob, tcxx::function exn_cob, const std::string& keyspace) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
thrift_validation::validate_keyspace_not_system(keyspace);
if (!_db.local().has_keyspace(keyspace)) {
throw NotFoundException();
}
return _query_state.get_client_state().has_keyspace_access(keyspace, auth::permission::DROP).then([=] {
return service::get_local_migration_manager().announce_keyspace_drop(keyspace, false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void system_update_keyspace(tcxx::function cob, tcxx::function exn_cob, const KsDef& ks_def) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
thrift_validation::validate_keyspace_not_system(ks_def.name);
if (!_db.local().has_keyspace(ks_def.name)) {
throw NotFoundException();
}
if (!ks_def.cf_defs.empty()) {
throw make_exception("Keyspace update must not contain any column family definitions.");
}
auto ksm = keyspace_from_thrift(ks_def);
return _query_state.get_client_state().has_keyspace_access(ks_def.name, auth::permission::ALTER).then([this, ksm = std::move(ksm)] {
return service::get_local_migration_manager().announce_keyspace_update(std::move(ksm), false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void system_update_column_family(tcxx::function cob, tcxx::function exn_cob, const CfDef& cf_def) {
with_cob(std::move(cob), std::move(exn_cob), [&] {
auto& cf = _db.local().find_column_family(cf_def.keyspace, cf_def.name);
auto schema = cf.schema();
if (schema->is_cql3_table()) {
throw make_exception("Cannot modify CQL3 table {} as it may break the schema. You should use cqlsh to modify CQL3 tables instead.", cf_def.name);
}
if (schema->is_view()) {
throw make_exception("Cannot modify Materialized View table %s as it may break the schema. "
"You should use cqlsh to modify Materialized View tables instead.", cf_def.name);
}
if (!cf.views().empty()) {
throw make_exception("Cannot modify table with Materialized Views %s as it may break the schema. "
"You should use cqlsh to modify Materialized View tables instead.", cf_def.name);
}
auto s = schema_from_thrift(cf_def, cf_def.keyspace, schema->id());
if (schema->thrift().is_dynamic() && s->regular_columns_count() > 1) {
fail(unimplemented::cause::MIXED_CF);
}
return _query_state.get_client_state().has_schema_access(*schema, auth::permission::ALTER).then([this, s = std::move(s)] {
return service::get_local_migration_manager().announce_column_family_update(std::move(s), true, {}, false).then([this] {
return std::string(_db.local().get_version().to_sstring());
});
});
});
}
void execute_cql_query(tcxx::function cob, tcxx::function exn_cob, const std::string& query, const Compression::type compression) {
throw make_exception("CQL2 is not supported");
}
class cql3_result_visitor final : public ::transport::messages::result_message::visitor {
CqlResult _result;
public:
const CqlResult& result() const {
return _result;
}
virtual void visit(const ::transport::messages::result_message::void_message&) override {
_result.__set_type(CqlResultType::VOID);
}
virtual void visit(const ::transport::messages::result_message::set_keyspace& m) override {
_result.__set_type(CqlResultType::VOID);
}
virtual void visit(const ::transport::messages::result_message::prepared::cql& m) override {
throw make_exception("Cannot convert prepared query result to CqlResult");
}
virtual void visit(const ::transport::messages::result_message::prepared::thrift& m) override {
throw make_exception("Cannot convert prepared query result to CqlResult");
}
virtual void visit(const ::transport::messages::result_message::schema_change& m) override {
_result.__set_type(CqlResultType::VOID);
}
virtual void visit(const ::transport::messages::result_message::rows& m) override {
_result = to_thrift_result(m.rs());
}
};
void execute_cql3_query(tcxx::function cob, tcxx::function exn_cob, const std::string& query, const Compression::type compression, const ConsistencyLevel::type consistency) {
with_exn_cob(std::move(exn_cob), [&] {
if (compression != Compression::type::NONE) {
throw make_exception("Compressed query strings are not supported");
}
auto opts = std::make_unique(cl_from_thrift(consistency), stdx::nullopt, std::vector(),
false, cql3::query_options::specific_options::DEFAULT, cql_serialization_format::latest());
auto f = _query_processor.local().process(query, _query_state, *opts);
return f.then([cob = std::move(cob), opts = std::move(opts)](auto&& ret) {
cql3_result_visitor visitor;
ret->accept(visitor);
return cob(visitor.result());
});
});
}
void prepare_cql_query(tcxx::function cob, tcxx::function exn_cob, const std::string& query, const Compression::type compression) {
throw make_exception("CQL2 is not supported");
}
class prepared_result_visitor final : public ::transport::messages::result_message::visitor_base {
CqlPreparedResult _result;
public:
const CqlPreparedResult& result() const {
return _result;
}
virtual void visit(const ::transport::messages::result_message::prepared::cql& m) override {
throw std::runtime_error("Unexpected result message type.");
}
virtual void visit(const ::transport::messages::result_message::prepared::thrift& m) override {
_result.__set_itemId(m.get_id());
auto& names = m.metadata()->names();
_result.__set_count(names.size());
std::vector variable_types;
std::vector variable_names;
for (auto csp : names) {
variable_types.emplace_back(csp->type->name());
variable_names.emplace_back(csp->name->to_string());
}
_result.__set_variable_types(std::move(variable_types));
_result.__set_variable_names(std::move(variable_names));
}
};
void prepare_cql3_query(tcxx::function cob, tcxx::function exn_cob, const std::string& query, const Compression::type compression) {
with_exn_cob(std::move(exn_cob), [&] {
validate_login();
if (compression != Compression::type::NONE) {
throw make_exception("Compressed query strings are not supported");
}
return _query_processor.local().prepare(query, _query_state).then([cob = std::move(cob)](auto&& stmt) {
prepared_result_visitor visitor;
stmt->accept(visitor);
cob(visitor.result());
});
});
}
void execute_prepared_cql_query(tcxx::function cob, tcxx::function exn_cob, const int32_t itemId, const std::vector & values) {
throw make_exception("CQL2 is not supported");
}
void execute_prepared_cql3_query(tcxx::function cob, tcxx::function exn_cob, const int32_t itemId, const std::vector & values, const ConsistencyLevel::type consistency) {
with_exn_cob(std::move(exn_cob), [&] {
auto prepared = _query_processor.local().get_prepared_for_thrift(itemId);
if (!prepared) {
throw make_exception("Prepared query with id %d not found", itemId);
}
auto stmt = prepared->statement;
if (stmt->get_bound_terms() != values.size()) {
throw make_exception("Wrong number of values specified. Expected %d, got %d.", stmt->get_bound_terms(), values.size());
}
std::vector bytes_values;
std::transform(values.begin(), values.end(), std::back_inserter(bytes_values), [](auto&& s) {
return cql3::raw_value::make_value(to_bytes(s));
});
auto opts = std::make_unique(cl_from_thrift(consistency), stdx::nullopt, std::move(bytes_values),
false, cql3::query_options::specific_options::DEFAULT, cql_serialization_format::latest());
auto f = _query_processor.local().process_statement(stmt, _query_state, *opts);
return f.then([cob = std::move(cob), opts = std::move(opts)](auto&& ret) {
cql3_result_visitor visitor;
ret->accept(visitor);
return cob(visitor.result());
});
});
}
void set_cql_version(tcxx::function cob, tcxx::function exn_cob, const std::string& version) {
// No-op.
cob();
}
private:
template
static sstring class_from_compound_type(const compound_type& ct) {
if (ct.is_singular()) {
return ct.types().front()->name();
}
sstring type = "org.apache.cassandra.db.marshal.CompositeType(";
for (auto& dt : ct.types()) {
type += dt->name();
if (&dt != &*ct.types().rbegin()) {
type += ",";
}
}
type += ")";
return type;
}
static std::pair, bool> get_types(const std::string& thrift_type) {
static const char composite_type[] = "CompositeType";
std::vector ret;
auto t = sstring_view(thrift_type);
auto composite_idx = t.find(composite_type);
bool is_compound = false;
if (composite_idx == sstring_view::npos) {
ret.emplace_back(db::marshal::type_parser::parse(t));
} else {
t.remove_prefix(composite_idx + sizeof(composite_type) - 1);
auto types = db::marshal::type_parser(t).get_type_parameters(false);
std::move(types.begin(), types.end(), std::back_inserter(ret));
is_compound = true;
}
return std::make_pair(std::move(ret), is_compound);
}
static CqlResult to_thrift_result(const cql3::result_set& rs) {
CqlResult result;
result.__set_type(CqlResultType::ROWS);
constexpr static const char* utf8 = "UTF8Type";
CqlMetadata mtd;
std::map name_types;
std::map value_types;
for (auto&& c : rs.get_metadata().get_names()) {
auto&& name = c->name->to_string();
name_types.emplace(name, utf8);
value_types.emplace(name, c->type->name());
}
mtd.__set_name_types(name_types);
mtd.__set_value_types(value_types);
mtd.__set_default_name_type(utf8);
mtd.__set_default_value_type(utf8);
result.__set_schema(mtd);
std::vector rows;
rows.reserve(rs.rows().size());
for (auto&& row : rs.rows()) {
std::vector columns;
columns.reserve(rs.get_metadata().column_count());
for (unsigned i = 0; i < row.size(); i++) { // iterator
auto& col = rs.get_metadata().get_names()[i];
Column c;
c.__set_name(col->name->to_string());
auto& data = row[i];
if (data) {
c.__set_value(bytes_to_string(*data));
}
columns.emplace_back(std::move(c));
}
CqlRow r;
r.__set_key(std::string());
r.__set_columns(columns);
rows.emplace_back(std::move(r));
}
result.__set_rows(rows);
return result;
}
static KsDef get_keyspace_definition(const keyspace& ks) {
auto make_options = [](auto&& m) {
return std::map(m.begin(), m.end());
};
auto&& meta = ks.metadata();
KsDef def;
def.__set_name(meta->name());
def.__set_strategy_class(meta->strategy_name());
def.__set_strategy_options(make_options(meta->strategy_options()));
std::vector cfs;
for (auto&& s : meta->tables()) {
if (s->is_cql3_table()) {
continue;
}
CfDef cf_def;
cf_def.__set_keyspace(s->ks_name());
cf_def.__set_name(s->cf_name());
cf_def.__set_column_type(cf_type_to_sstring(s->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(s->regular_column_name_type()->name());
}
cf_def.__set_comment(s->comment());
cf_def.__set_read_repair_chance(s->read_repair_chance());
std::vector columns;
if (!s->thrift().is_dynamic()) {
for (auto&& c : s->regular_columns()) {
ColumnDef c_def;
c_def.__set_name(c.name_as_text());
c_def.__set_validation_class(c.type->name());
columns.emplace_back(std::move(c_def));
}
}
cf_def.__set_column_metadata(columns);
cf_def.__set_gc_grace_seconds(s->gc_grace_seconds().count());
cf_def.__set_default_validation_class(s->default_validator()->name());
cf_def.__set_min_compaction_threshold(s->min_compaction_threshold());
cf_def.__set_max_compaction_threshold(s->max_compaction_threshold());
cf_def.__set_key_validation_class(class_from_compound_type(*s->partition_key_type()));
cf_def.__set_key_alias(s->partition_key_columns().begin()->name_as_text());
cf_def.__set_compaction_strategy(sstables::compaction_strategy::name(s->compaction_strategy()));
cf_def.__set_compaction_strategy_options(make_options(s->compaction_strategy_options()));
cf_def.__set_compression_options(make_options(s->get_compressor_params().get_options()));
cf_def.__set_bloom_filter_fp_chance(s->bloom_filter_fp_chance());
cf_def.__set_caching("all");
cf_def.__set_dclocal_read_repair_chance(s->dc_local_read_repair_chance());
cf_def.__set_memtable_flush_period_in_ms(s->memtable_flush_period());
cf_def.__set_default_time_to_live(s->default_time_to_live().count());
cf_def.__set_speculative_retry(s->speculative_retry().to_sstring());
cfs.emplace_back(std::move(cf_def));
}
def.__set_cf_defs(cfs);
def.__set_durable_writes(meta->durable_writes());
return std::move(def);
}
static index_info index_info_from_thrift(const ColumnDef& def) {
stdx::optional idx_name;
stdx::optional> idx_opts;
auto idx_type = ::index_type::none;
if (def.__isset.index_type) {
idx_type = [&def] {
switch (def.index_type) {
case IndexType::type::KEYS: return ::index_type::keys;
case IndexType::type::COMPOSITES: return ::index_type::composites;
case IndexType::type::CUSTOM: return ::index_type::custom;
default: return ::index_type::none;
};
}();
}
if (def.__isset.index_name) {
idx_name = to_sstring(def.index_name);
}
if (def.__isset.index_options) {
idx_opts = std::unordered_map(def.index_options.begin(), def.index_options.end());
}
return index_info(idx_type, idx_name, idx_opts);
}
static schema_ptr schema_from_thrift(const CfDef& cf_def, const sstring ks_name, std::experimental::optional id = { }) {
thrift_validation::validate_cf_def(cf_def);
schema_builder builder(ks_name, cf_def.name, id);
if (cf_def.__isset.key_validation_class) {
auto pk_types = std::move(get_types(cf_def.key_validation_class).first);
if (pk_types.size() == 1 && cf_def.__isset.key_alias) {
builder.with_column(to_bytes(cf_def.key_alias), std::move(pk_types.back()), column_kind::partition_key);
} else {
for (uint32_t i = 0; i < pk_types.size(); ++i) {
builder.with_column(to_bytes(sprint("key%d", i + 1)), std::move(pk_types[i]), column_kind::partition_key);
}
}
} else {
builder.with_column(to_bytes("key"), bytes_type, column_kind::partition_key);
}
data_type regular_column_name_type;
if (cf_def.column_metadata.empty()) {
// Dynamic CF
builder.set_is_dense(true);
regular_column_name_type = utf8_type;
auto p = get_types(cf_def.comparator_type);
auto ck_types = std::move(p.first);
builder.set_is_compound(p.second);
for (uint32_t i = 0; i < ck_types.size(); ++i) {
builder.with_column(to_bytes(sprint("column%d", i + 1)), std::move(ck_types[i]), column_kind::clustering_key);
}
auto&& vtype = cf_def.__isset.default_validation_class
? db::marshal::type_parser::parse(to_sstring(cf_def.default_validation_class))
: bytes_type;
builder.set_default_validator(vtype);
builder.with_column(to_bytes("value"), std::move(vtype));
} else {
// Static CF
builder.set_is_compound(false);
regular_column_name_type = db::marshal::type_parser::parse(to_sstring(cf_def.comparator_type));
for (const ColumnDef& col_def : cf_def.column_metadata) {
auto col_name = to_bytes(col_def.name);
regular_column_name_type->validate(col_name);
builder.with_column(std::move(col_name), db::marshal::type_parser::parse(to_sstring(col_def.validation_class)),
index_info_from_thrift(col_def), column_kind::regular_column);
}
}
builder.set_regular_column_name_type(regular_column_name_type);
if (cf_def.__isset.comment) {
builder.set_comment(cf_def.comment);
}
if (cf_def.__isset.read_repair_chance) {
builder.set_read_repair_chance(cf_def.read_repair_chance);
}
if (cf_def.__isset.gc_grace_seconds) {
builder.set_gc_grace_seconds(cf_def.gc_grace_seconds);
}
if (cf_def.__isset.min_compaction_threshold) {
builder.set_min_compaction_threshold(cf_def.min_compaction_threshold);
}
if (cf_def.__isset.max_compaction_threshold) {
builder.set_max_compaction_threshold(cf_def.max_compaction_threshold);
}
if (cf_def.__isset.compaction_strategy) {
builder.set_compaction_strategy(sstables::compaction_strategy::type(cf_def.compaction_strategy));
}
auto make_options = [](const std::map& m) {
return std::map{m.begin(), m.end()};
};
if (cf_def.__isset.compaction_strategy_options) {
builder.set_compaction_strategy_options(make_options(cf_def.compaction_strategy_options));
}
if (cf_def.__isset.compression_options) {
builder.set_compressor_params(compression_parameters(make_options(cf_def.compression_options)));
}
if (cf_def.__isset.bloom_filter_fp_chance) {
builder.set_bloom_filter_fp_chance(cf_def.bloom_filter_fp_chance);
}
if (cf_def.__isset.dclocal_read_repair_chance) {
builder.set_dc_local_read_repair_chance(cf_def.dclocal_read_repair_chance);
}
if (cf_def.__isset.memtable_flush_period_in_ms) {
builder.set_memtable_flush_period(cf_def.memtable_flush_period_in_ms);
}
if (cf_def.__isset.default_time_to_live) {
builder.set_default_time_to_live(gc_clock::duration(cf_def.default_time_to_live));
}
if (cf_def.__isset.speculative_retry) {
builder.set_speculative_retry(cf_def.speculative_retry);
}
if (cf_def.__isset.min_index_interval) {
builder.set_min_index_interval(cf_def.min_index_interval);
}
if (cf_def.__isset.max_index_interval) {
builder.set_max_index_interval(cf_def.max_index_interval);
}
return builder.build();
}
static lw_shared_ptr keyspace_from_thrift(const KsDef& ks_def) {
thrift_validation::validate_ks_def(ks_def);
std::vector cf_defs;
cf_defs.reserve(ks_def.cf_defs.size());
for (const CfDef& cf_def : ks_def.cf_defs) {
if (cf_def.keyspace != ks_def.name) {
throw make_exception("CfDef (%s) had a keyspace definition that did not match KsDef", cf_def.keyspace);
}
cf_defs.emplace_back(schema_from_thrift(cf_def, ks_def.name));
}
return make_lw_shared(
to_sstring(ks_def.name),
to_sstring(ks_def.strategy_class),
std::map{ks_def.strategy_options.begin(), ks_def.strategy_options.end()},
ks_def.durable_writes,
std::move(cf_defs));
}
static schema_ptr lookup_schema(database& db, const sstring& ks_name, const sstring& cf_name) {
if (ks_name.empty()) {
throw make_exception("keyspace not set");
}
return db.find_schema(ks_name, cf_name);
}
static partition_key key_from_thrift(const schema& s, bytes_view k) {
thrift_validation::validate_key(s, k);
if (s.partition_key_size() == 1) {
return partition_key::from_single_value(s, to_bytes(k));
}
auto composite = composite_view(k);
return partition_key::from_exploded(composite.values());
}
static db::consistency_level cl_from_thrift(const ConsistencyLevel::type consistency_level) {
switch(consistency_level) {
case ConsistencyLevel::type::ONE: return db::consistency_level::ONE;
case ConsistencyLevel::type::QUORUM: return db::consistency_level::QUORUM;
case ConsistencyLevel::type::LOCAL_QUORUM: return db::consistency_level::LOCAL_QUORUM;
case ConsistencyLevel::type::EACH_QUORUM: return db::consistency_level::EACH_QUORUM;
case ConsistencyLevel::type::ALL: return db::consistency_level::ALL;
case ConsistencyLevel::type::ANY: return db::consistency_level::ANY;
case ConsistencyLevel::type::TWO: return db::consistency_level::TWO;
case ConsistencyLevel::type::THREE: return db::consistency_level::THREE;
case ConsistencyLevel::type::SERIAL: return db::consistency_level::SERIAL;
case ConsistencyLevel::type::LOCAL_SERIAL: return db::consistency_level::LOCAL_SERIAL;
case ConsistencyLevel::type::LOCAL_ONE: return db::consistency_level::LOCAL_ONE;
default: throw make_exception("undefined consistency_level %s", consistency_level);
}
}
static ttl_opt maybe_ttl(const schema& s, const Column& col) {
if (col.__isset.ttl) {
auto ttl = std::chrono::duration_cast(std::chrono::seconds(col.ttl));
if (ttl.count() <= 0) {
throw make_exception("ttl must be positive");
}
if (ttl > max_ttl) {
throw make_exception("ttl is too large");
}
return {ttl};
} else if (s.default_time_to_live().count() > 0) {
return {s.default_time_to_live()};
} else {
return { };
}
}
static clustering_key_prefix make_clustering_prefix(const schema& s, bytes_view v) {
auto composite = composite_view(v, s.thrift().has_compound_comparator());
return clustering_key_prefix::from_exploded(composite.values());
}
static query::clustering_range::bound make_clustering_bound(const schema& s, bytes_view v, composite::eoc exclusiveness_marker) {
auto composite = composite_view(v, s.thrift().has_compound_comparator());
auto last = composite::eoc::none;
auto&& ck = clustering_key_prefix::from_exploded(composite.components() | boost::adaptors::transformed([&](auto&& c) {
last = c.second;
return c.first;
}));
return query::clustering_range::bound(std::move(ck), last != exclusiveness_marker);
}
static range make_clustering_range(const schema& s, const std::string& start, const std::string& end) {
using bound = range::bound;
stdx::optional start_bound;
if (!start.empty()) {
start_bound = make_clustering_bound(s, to_bytes_view(start), composite::eoc::end);
}
stdx::optional end_bound;
if (!end.empty()) {
end_bound = make_clustering_bound(s, to_bytes_view(end), composite::eoc::start);
}
return { std::move(start_bound), std::move(end_bound) };
}
static query::clustering_range make_clustering_range_and_validate(const schema& s, const std::string& start, const std::string& end) {
auto range = make_clustering_range(s, start, end);
auto bounds = bound_view::from_range(range);
if (bound_view::compare(s)(bounds.second, bounds.first)) {
throw make_exception("Range finish must come after start in the order of traversal");
}
return query::clustering_range(std::move(range));
}
static range make_range(const std::string& start, const std::string& end) {
using bound = range::bound;
stdx::optional start_bound;
if (!start.empty()) {
start_bound = bound(to_bytes(start));
}
stdx::optional end_bound;
if (!end.empty()) {
end_bound = bound(to_bytes(end));
}
return { std::move(start_bound), std::move(end_bound) };
}
static std::pair make_column_range(const schema& s, const std::string& start, const std::string& end) {
auto start_it = start.empty() ? s.regular_begin() : s.regular_lower_bound(to_bytes(start));
auto end_it = end.empty() ? s.regular_end() : s.regular_upper_bound(to_bytes(end));
if (start_it > end_it) {
throw make_exception("Range finish must come after start in the order of traversal");
}
return std::make_pair(std::move(start_it), std::move(end_it));
}
// Adds the column_ids from the specified range of column_definitions to the out vector,
// according to the order defined by reversed.
template
static std::vector add_columns(Iterator beg, Iterator end, bool reversed) {
auto range = boost::make_iterator_range(std::move(beg), std::move(end))
| boost::adaptors::filtered(std::mem_fn(&column_definition::is_atomic))
| boost::adaptors::transformed(std::mem_fn(&column_definition::id));
return reversed ? boost::copy_range>(range | boost::adaptors::reversed)
: boost::copy_range>(range);
}
static query::partition_slice::option_set query_opts(const schema& s) {
query::partition_slice::option_set opts;
if (!s.is_counter()) {
opts.set(query::partition_slice::option::send_timestamp);
opts.set(query::partition_slice::option::send_ttl);
}
if (s.thrift().is_dynamic()) {
opts.set(query::partition_slice::option::send_clustering_key);
}
opts.set(query::partition_slice::option::send_partition_key);
return opts;
}
static lw_shared_ptr slice_pred_to_read_cmd(const schema& s, const SlicePredicate& predicate) {
auto opts = query_opts(s);
std::vector clustering_ranges;
std::vector regular_columns;
uint32_t per_partition_row_limit = query::max_rows;
if (predicate.__isset.column_names) {
thrift_validation::validate_column_names(predicate.column_names);
auto unique_column_names = boost::copy_range>(predicate.column_names | boost::adaptors::uniqued);
if (s.thrift().is_dynamic()) {
for (auto&& name : unique_column_names) {
auto ckey = make_clustering_prefix(s, to_bytes(name));
clustering_ranges.emplace_back(query::clustering_range::make_singular(std::move(ckey)));
}
regular_columns.emplace_back(s.regular_begin()->id);
} else {
clustering_ranges.emplace_back(query::clustering_range::make_open_ended_both_sides());
auto&& defs = unique_column_names
| boost::adaptors::transformed([&s](auto&& name) { return s.get_column_definition(to_bytes(name)); })
| boost::adaptors::filtered([](auto* def) { return def; })
| boost::adaptors::indirected;
regular_columns = add_columns(defs.begin(), defs.end(), false);
}
} else if (predicate.__isset.slice_range) {
auto range = predicate.slice_range;
if (range.count < 0) {
throw make_exception("SliceRange requires non-negative count");
}
if (range.reversed) {
std::swap(range.start, range.finish);
opts.set(query::partition_slice::option::reversed);
}
per_partition_row_limit = static_cast(range.count);
if (s.thrift().is_dynamic()) {
clustering_ranges.emplace_back(make_clustering_range_and_validate(s, range.start, range.finish));
regular_columns.emplace_back(s.regular_begin()->id);
} else {
clustering_ranges.emplace_back(query::clustering_range::make_open_ended_both_sides());
auto r = make_column_range(s, range.start, range.finish);
// For static CFs, the limit is enforced on the result as we do not implement
// a cell limit in the database engine.
regular_columns = add_columns(r.first, r.second, range.reversed);
}
} else {
throw make_exception("SlicePredicate column_names and slice_range may not both be null");
}
auto slice = query::partition_slice(std::move(clustering_ranges), {}, std::move(regular_columns), opts,
nullptr, cql_serialization_format::internal(), per_partition_row_limit);
return make_lw_shared(s.id(), s.version(), std::move(slice));
}
static ColumnParent column_path_to_column_parent(const ColumnPath& column_path) {
ColumnParent ret;
ret.__set_column_family(column_path.column_family);
if (column_path.__isset.super_column) {
ret.__set_super_column(column_path.super_column);
}
return ret;
}
static SlicePredicate column_path_to_slice_predicate(const ColumnPath& column_path) {
SlicePredicate ret;
if (column_path.__isset.column) {
ret.__set_column_names({column_path.column});
}
return ret;
}
static dht::partition_range_vector make_partition_ranges(const schema& s, const std::vector& keys) {
dht::partition_range_vector ranges;
for (auto&& key : keys) {
auto pk = key_from_thrift(s, to_bytes_view(key));
auto dk = dht::global_partitioner().decorate_key(s, pk);
ranges.emplace_back(dht::partition_range::make_singular(std::move(dk)));
}
return ranges;
}
static Column make_column(const bytes& col, const query::result_atomic_cell_view& cell) {
Column ret;
ret.__set_name(bytes_to_string(col));
ret.__set_value(bytes_to_string(cell.value()));
ret.__set_timestamp(cell.timestamp());
if (cell.ttl()) {
ret.__set_ttl(cell.ttl()->count());
}
return ret;
}
static ColumnOrSuperColumn column_to_column_or_supercolumn(Column&& col) {
ColumnOrSuperColumn ret;
ret.__set_column(std::move(col));
return ret;
}
static ColumnOrSuperColumn make_column_or_supercolumn(const bytes& col, const query::result_atomic_cell_view& cell) {
return column_to_column_or_supercolumn(make_column(col, cell));
}
static CounterColumn make_counter_column(const bytes& col, const query::result_atomic_cell_view& cell) {
CounterColumn ret;
ret.__set_name(bytes_to_string(col));
ret.__set_value(value_cast(long_type->deserialize_value(cell.value())));
return ret;
}
static ColumnOrSuperColumn counter_column_to_column_or_supercolumn(CounterColumn&& col) {
ColumnOrSuperColumn ret;
ret.__set_counter_column(std::move(col));
return ret;
}
static ColumnOrSuperColumn make_counter_column_or_supercolumn(const bytes& col, const query::result_atomic_cell_view& cell) {
return counter_column_to_column_or_supercolumn(make_counter_column(col, cell));
}
static std::string partition_key_to_string(const schema& s, const partition_key& key) {
return bytes_to_string(to_legacy(*s.partition_key_type(), key));
}
template
GCC6_CONCEPT( requires thrift::Aggregator