data_value de-any-fication
We use boost::any to convert to and from database values (stored in serlialized form) and native C++ values. boost::any captures information about the data type (how to copy/move/delete etc.) and stores it inside the boost::any instance. We later retrieve the real value using boost::any_cast. However, data_value (which has a boost::any member) already has type information as a data_type instance. By teaching data_type intances about the corresponding native type, we can elimiante the use of boost::any. While boost::any is evil and eliminating it improves efficiency somewhat, the real goal is growing native type support in data_type. We will use that later to store native types in the cache, enabling O(log n) access to collections, O(1) access to tuples, and more efficient large blob support.
This commit is contained in:
committed by
Tomasz Grabiec
parent
b478d9d8a6
commit
2c3591cbd9
@@ -159,7 +159,7 @@ public:
|
||||
}
|
||||
return ::serialize_value(*this, values);
|
||||
}
|
||||
bytes serialize_value_deep(const std::vector<boost::any>& values) {
|
||||
bytes serialize_value_deep(const std::vector<data_value>& values) {
|
||||
// TODO: Optimize
|
||||
std::vector<bytes> partial;
|
||||
partial.reserve(values.size());
|
||||
|
||||
@@ -80,7 +80,7 @@ int64_t attributes::get_timestamp(int64_t now, const query_options& options) {
|
||||
} catch (marshal_exception e) {
|
||||
throw exceptions::invalid_request_exception("Invalid timestamp value");
|
||||
}
|
||||
return boost::any_cast<int64_t>(data_type_for<int64_t>()->deserialize(*tval));
|
||||
return value_cast<int64_t>(data_type_for<int64_t>()->deserialize(*tval));
|
||||
}
|
||||
|
||||
int32_t attributes::get_time_to_live(const query_options& options) {
|
||||
@@ -99,7 +99,7 @@ int32_t attributes::get_time_to_live(const query_options& options) {
|
||||
throw exceptions::invalid_request_exception("Invalid TTL value");
|
||||
}
|
||||
|
||||
auto ttl = boost::any_cast<int32_t>(data_type_for<int32_t>()->deserialize(*tval));
|
||||
auto ttl = value_cast<int32_t>(data_type_for<int32_t>()->deserialize(*tval));
|
||||
if (ttl < 0) {
|
||||
throw exceptions::invalid_request_exception("A TTL must be greater or equal to 0");
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
if (!values[0]) {
|
||||
return;
|
||||
}
|
||||
_sum += boost::any_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
_sum += value_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
return;
|
||||
}
|
||||
++_count;
|
||||
_sum += boost::any_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
_sum += value_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
if (!values[0]) {
|
||||
return;
|
||||
}
|
||||
auto val = boost::any_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
auto val = value_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
if (!_max) {
|
||||
_max = val;
|
||||
} else {
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
if (!values[0]) {
|
||||
return;
|
||||
}
|
||||
auto val = boost::any_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
auto val = value_cast<Type>(data_type_for<Type>()->deserialize(*values[0]));
|
||||
if (!_min) {
|
||||
_min = val;
|
||||
} else {
|
||||
|
||||
@@ -71,10 +71,10 @@ make_min_timeuuid_fct() {
|
||||
return {};
|
||||
}
|
||||
auto ts_obj = timestamp_type->deserialize(*bb);
|
||||
if (ts_obj.empty()) {
|
||||
if (ts_obj.is_null()) {
|
||||
return {};
|
||||
}
|
||||
auto ts = boost::any_cast<db_clock::time_point>(ts_obj);
|
||||
auto ts = value_cast<db_clock::time_point>(ts_obj);
|
||||
auto uuid = utils::UUID_gen::min_time_UUID(ts.time_since_epoch().count());
|
||||
return {timeuuid_type->decompose(uuid)};
|
||||
});
|
||||
@@ -91,10 +91,10 @@ make_max_timeuuid_fct() {
|
||||
return {};
|
||||
}
|
||||
auto ts_obj = timestamp_type->deserialize(*bb);
|
||||
if (ts_obj.empty()) {
|
||||
if (ts_obj.is_null()) {
|
||||
return {};
|
||||
}
|
||||
auto ts = boost::any_cast<db_clock::time_point>(ts_obj);
|
||||
auto ts = value_cast<db_clock::time_point>(ts_obj);
|
||||
auto uuid = utils::UUID_gen::max_time_UUID(ts.time_since_epoch().count());
|
||||
return {timeuuid_type->decompose(uuid)};
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ shared_ptr<function>
|
||||
make_uuid_fct() {
|
||||
return make_native_scalar_function<false>("uuid", uuid_type, {},
|
||||
[] (serialization_format sf, const std::vector<bytes_opt>& parameters) -> bytes_opt {
|
||||
return {uuid_type->decompose(boost::any(utils::make_random_uuid()))};
|
||||
return {uuid_type->decompose(utils::make_random_uuid())};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -113,12 +113,12 @@ lists::value::from_serialized(bytes_view v, list_type type, serialization_format
|
||||
// Collections have this small hack that validate cannot be called on a serialized object,
|
||||
// but compose does the validation (so we're fine).
|
||||
// FIXME: deserializeForNativeProtocol()?!
|
||||
auto l = boost::any_cast<list_type_impl::native_type>(type->deserialize(v, sf));
|
||||
auto l = value_cast<list_type_impl::native_type>(type->deserialize(v, sf));
|
||||
std::vector<bytes_opt> elements;
|
||||
elements.reserve(l.size());
|
||||
for (auto&& element : l) {
|
||||
// elements can be null in lists that represent a set of IN values
|
||||
elements.push_back(element.empty() ? bytes_opt() : bytes_opt(type->get_elements_type()->decompose(element)));
|
||||
elements.push_back(element.is_null() ? bytes_opt() : bytes_opt(type->get_elements_type()->decompose(element)));
|
||||
}
|
||||
return value(std::move(elements));
|
||||
} catch (marshal_exception& e) {
|
||||
|
||||
@@ -161,7 +161,7 @@ maps::value::from_serialized(bytes_view value, map_type type, serialization_form
|
||||
// Collections have this small hack that validate cannot be called on a serialized object,
|
||||
// but compose does the validation (so we're fine).
|
||||
// FIXME: deserialize_for_native_protocol?!
|
||||
auto m = boost::any_cast<map_type_impl::native_type>(type->deserialize(value, sf));
|
||||
auto m = value_cast<map_type_impl::native_type>(type->deserialize(value, sf));
|
||||
std::map<bytes, bytes, serialized_compare> map(type->get_keys_type()->as_less_comparator());
|
||||
for (auto&& e : m) {
|
||||
map.emplace(type->get_keys_type()->decompose(e.first),
|
||||
|
||||
@@ -302,7 +302,7 @@ query_processor::parse_statement(const sstring_view& query)
|
||||
|
||||
query_options query_processor::make_internal_options(
|
||||
::shared_ptr<statements::parsed_statement::prepared> p,
|
||||
const std::initializer_list<boost::any>& values) {
|
||||
const std::initializer_list<data_value>& values) {
|
||||
if (p->bound_names.size() != values.size()) {
|
||||
throw std::invalid_argument(sprint("Invalid number of values. Expecting %d but got %d", p->bound_names.size(), values.size()));
|
||||
}
|
||||
@@ -310,9 +310,9 @@ query_options query_processor::make_internal_options(
|
||||
std::vector<bytes_opt> bound_values;
|
||||
for (auto& v : values) {
|
||||
auto& n = *ni++;
|
||||
if (v.type() == typeid(bytes)) {
|
||||
bound_values.push_back({boost::any_cast<bytes>(v)});
|
||||
} else if (v.empty()) {
|
||||
if (v.type() == bytes_type) {
|
||||
bound_values.push_back({value_cast<bytes>(v)});
|
||||
} else if (v.is_null()) {
|
||||
bound_values.push_back({});
|
||||
} else {
|
||||
bound_values.push_back({n->type->decompose(v)});
|
||||
@@ -335,7 +335,7 @@ query_options query_processor::make_internal_options(
|
||||
|
||||
future<::shared_ptr<untyped_result_set>> query_processor::execute_internal(
|
||||
const std::experimental::string_view& query_string,
|
||||
const std::initializer_list<boost::any>& values) {
|
||||
const std::initializer_list<data_value>& values) {
|
||||
auto p = prepare_internal(query_string);
|
||||
auto opts = make_internal_options(p, values);
|
||||
return do_with(std::move(opts),
|
||||
|
||||
@@ -323,12 +323,12 @@ public:
|
||||
#endif
|
||||
private:
|
||||
::shared_ptr<statements::parsed_statement::prepared> prepare_internal(const std::experimental::string_view& query);
|
||||
query_options make_internal_options(::shared_ptr<statements::parsed_statement::prepared>, const std::initializer_list<boost::any>&);
|
||||
query_options make_internal_options(::shared_ptr<statements::parsed_statement::prepared>, const std::initializer_list<data_value>&);
|
||||
|
||||
public:
|
||||
future<::shared_ptr<untyped_result_set>> execute_internal(
|
||||
const std::experimental::string_view& query_string,
|
||||
const std::initializer_list<boost::any>& = { });
|
||||
const std::initializer_list<data_value>& = { });
|
||||
|
||||
/*
|
||||
* This function provides a timestamp that is guaranteed to be higher than any timestamp
|
||||
|
||||
@@ -125,7 +125,7 @@ sets::value::from_serialized(bytes_view v, set_type type, serialization_format s
|
||||
// Collections have this small hack that validate cannot be called on a serialized object,
|
||||
// but compose does the validation (so we're fine).
|
||||
// FIXME: deserializeForNativeProtocol?!
|
||||
auto s = boost::any_cast<set_type_impl::native_type>(type->deserialize(v, sf));
|
||||
auto s = value_cast<set_type_impl::native_type>(type->deserialize(v, sf));
|
||||
std::set<bytes, serialized_compare> elements(type->get_elements_type()->as_less_comparator());
|
||||
for (auto&& element : s) {
|
||||
elements.insert(elements.end(), type->get_elements_type()->decompose(element));
|
||||
|
||||
@@ -160,7 +160,7 @@ int32_t select_statement::get_limit(const query_options& options) const {
|
||||
|
||||
try {
|
||||
int32_type->validate(*val);
|
||||
auto l = boost::any_cast<int32_t>(int32_type->deserialize(*val));
|
||||
auto l = value_cast<int32_t>(int32_type->deserialize(*val));
|
||||
if (l <= 0) {
|
||||
throw exceptions::invalid_request_exception("LIMIT must be strictly positive");
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ public:
|
||||
try {
|
||||
// Collections have this small hack that validate cannot be called on a serialized object,
|
||||
// but the deserialization does the validation (so we're fine).
|
||||
auto l = boost::any_cast<list_type_impl::native_type>(type->deserialize(value, options.get_serialization_format()));
|
||||
auto l = value_cast<list_type_impl::native_type>(type->deserialize(value, options.get_serialization_format()));
|
||||
auto ttype = dynamic_pointer_cast<const tuple_type_impl>(type->get_elements_type());
|
||||
assert(ttype);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
}
|
||||
template<typename T>
|
||||
T get_as(const sstring& name) const {
|
||||
return boost::any_cast<T>(data_type_for<T>()->deserialize(get_blob(name)));
|
||||
return value_cast<T>(data_type_for<T>()->deserialize(get_blob(name)));
|
||||
}
|
||||
// this could maybe be done as an overload of get_as (or something), but that just
|
||||
// muddles things for no real gain. Let user (us) attempt to know what he is doing instead.
|
||||
@@ -75,12 +75,12 @@ public:
|
||||
data_type_for<K>(), data_type valtype =
|
||||
data_type_for<V>()) const {
|
||||
auto vec =
|
||||
boost::any_cast<const map_type_impl::native_type&>(
|
||||
value_cast<map_type_impl::native_type>(
|
||||
map_type_impl::get_instance(keytype, valtype, false)->deserialize(
|
||||
get_blob(name)));
|
||||
std::transform(vec.begin(), vec.end(), out,
|
||||
[](auto& p) {
|
||||
return std::pair<K, V>(boost::any_cast<const K&>(p.first), boost::any_cast<const V&>(p.second));
|
||||
return std::pair<K, V>(value_cast<K>(p.first), value_cast<V>(p.second));
|
||||
});
|
||||
}
|
||||
template<typename K, typename V, typename ... Rest>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "core/fstream.hh"
|
||||
#include "core/do_with.hh"
|
||||
#include "log.hh"
|
||||
#include <boost/any.hpp>
|
||||
|
||||
static logging::logger logger("config");
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ struct query_context {
|
||||
future<::shared_ptr<cql3::untyped_result_set>> execute_cql(sstring text, sstring cf, Args&&... args) {
|
||||
// FIXME: Would be better not to use sprint here.
|
||||
sstring req = sprint(text, cf);
|
||||
return this->_qp.local().execute_internal(req, { boost::any(std::forward<Args>(args))... });
|
||||
return this->_qp.local().execute_internal(req, { data_value(std::forward<Args>(args))... });
|
||||
}
|
||||
database& db() {
|
||||
return _db.local();
|
||||
|
||||
@@ -329,7 +329,7 @@ future<utils::UUID> calculate_schema_digest(distributed<service::storage_proxy>&
|
||||
std::vector<query::result> results;
|
||||
for (auto&& p : rs->partitions()) {
|
||||
auto mut = p.mut().unfreeze(s);
|
||||
auto partition_key = boost::any_cast<sstring>(utf8_type->deserialize(mut.key().get_component(*s, 0)));
|
||||
auto partition_key = value_cast<sstring>(utf8_type->deserialize(mut.key().get_component(*s, 0)));
|
||||
if (partition_key == system_keyspace::NAME) {
|
||||
continue;
|
||||
}
|
||||
@@ -368,7 +368,7 @@ future<std::vector<frozen_mutation>> convert_schema_to_mutations(distributed<ser
|
||||
std::vector<frozen_mutation> results;
|
||||
for (auto&& p : rs->partitions()) {
|
||||
auto mut = p.mut().unfreeze(s);
|
||||
auto partition_key = boost::any_cast<sstring>(utf8_type->deserialize(mut.key().get_component(*s, 0)));
|
||||
auto partition_key = value_cast<sstring>(utf8_type->deserialize(mut.key().get_component(*s, 0)));
|
||||
if (partition_key == system_keyspace::NAME) {
|
||||
continue;
|
||||
}
|
||||
@@ -468,7 +468,7 @@ future<> do_merge_schema(distributed<service::storage_proxy>& proxy, std::vector
|
||||
std::set<sstring> keyspaces;
|
||||
std::set<utils::UUID> column_families;
|
||||
for (auto&& mutation : mutations) {
|
||||
keyspaces.emplace(boost::any_cast<sstring>(utf8_type->deserialize(mutation.key().get_component(*s, 0))));
|
||||
keyspaces.emplace(value_cast<sstring>(utf8_type->deserialize(mutation.key().get_component(*s, 0))));
|
||||
column_families.emplace(mutation.column_family_id());
|
||||
}
|
||||
|
||||
|
||||
@@ -540,10 +540,11 @@ future<> save_truncation_records(const column_family& cf, db_clock::time_point t
|
||||
out.write<db_clock::rep>(truncated_at.time_since_epoch().count());
|
||||
|
||||
map_type_impl::native_type tmp;
|
||||
tmp.emplace_back(boost::any{ cf.schema()->id() }, boost::any{ buf });
|
||||
tmp.emplace_back(cf.schema()->id(), buf);
|
||||
auto map_type = map_type_impl::get_instance(uuid_type, bytes_type, true);
|
||||
|
||||
sstring req = sprint("UPDATE system.%s SET truncated_at = truncated_at + ? WHERE key = '%s'", LOCAL, LOCAL);
|
||||
return qctx->qp().execute_internal(req, {tmp}).then([](auto rs) {
|
||||
return qctx->qp().execute_internal(req, {make_map_value(map_type, tmp)}).then([](auto rs) {
|
||||
truncation_records = {};
|
||||
return force_blocking_flush(LOCAL);
|
||||
});
|
||||
@@ -633,7 +634,7 @@ future<db_clock::time_point> get_truncated_at(utils::UUID cf_id) {
|
||||
set_type_impl::native_type prepare_tokens(std::unordered_set<dht::token>& tokens) {
|
||||
set_type_impl::native_type tset;
|
||||
for (auto& t: tokens) {
|
||||
tset.push_back(boost::any(dht::global_partitioner().to_sstring(t)));
|
||||
tset.push_back(dht::global_partitioner().to_sstring(t));
|
||||
}
|
||||
return tset;
|
||||
}
|
||||
@@ -641,7 +642,7 @@ set_type_impl::native_type prepare_tokens(std::unordered_set<dht::token>& tokens
|
||||
std::unordered_set<dht::token> decode_tokens(set_type_impl::native_type& tokens) {
|
||||
std::unordered_set<dht::token> tset;
|
||||
for (auto& t: tokens) {
|
||||
auto str = boost::any_cast<sstring>(t);
|
||||
auto str = value_cast<sstring>(t);
|
||||
assert(str == dht::global_partitioner().to_sstring(dht::global_partitioner().from_sstring(str)));
|
||||
tset.insert(dht::global_partitioner().from_sstring(str));
|
||||
}
|
||||
@@ -658,7 +659,8 @@ future<> update_tokens(gms::inet_address ep, std::unordered_set<dht::token> toke
|
||||
}
|
||||
|
||||
sstring req = "INSERT INTO system.%s (peer, tokens) VALUES (?, ?)";
|
||||
return execute_cql(req, PEERS, ep.addr(), prepare_tokens(tokens)).discard_result().then([] {
|
||||
auto set_type = set_type_impl::get_instance(utf8_type, true);
|
||||
return execute_cql(req, PEERS, ep.addr(), make_set_value(set_type, prepare_tokens(tokens))).discard_result().then([] {
|
||||
return force_blocking_flush(PEERS);
|
||||
});
|
||||
}
|
||||
@@ -689,7 +691,7 @@ future<std::unordered_map<gms::inet_address, std::unordered_set<dht::token>>> lo
|
||||
auto blob = row.get_blob("tokens");
|
||||
auto cdef = peers()->get_column_definition("tokens");
|
||||
auto deserialized = cdef->type->deserialize(blob);
|
||||
auto tokens = boost::any_cast<set_type_impl::native_type>(deserialized);
|
||||
auto tokens = value_cast<set_type_impl::native_type>(deserialized);
|
||||
|
||||
ret->emplace(peer, decode_tokens(tokens));
|
||||
}
|
||||
@@ -808,7 +810,8 @@ future<> update_tokens(std::unordered_set<dht::token> tokens) {
|
||||
}
|
||||
|
||||
sstring req = "INSERT INTO system.%s (key, tokens) VALUES (?, ?)";
|
||||
return execute_cql(req, LOCAL, sstring(LOCAL), prepare_tokens(tokens)).discard_result().then([] {
|
||||
auto set_type = set_type_impl::get_instance(utf8_type, true);
|
||||
return execute_cql(req, LOCAL, sstring(LOCAL), make_set_value(set_type, prepare_tokens(tokens))).discard_result().then([] {
|
||||
return force_blocking_flush(LOCAL);
|
||||
});
|
||||
}
|
||||
@@ -862,7 +865,7 @@ future<std::unordered_set<dht::token>> get_saved_tokens() {
|
||||
auto blob = msg->one().get_blob("tokens");
|
||||
auto cdef = local()->get_column_definition("tokens");
|
||||
auto deserialized = cdef->type->deserialize(blob);
|
||||
auto tokens = boost::any_cast<set_type_impl::native_type>(deserialized);
|
||||
auto tokens = value_cast<set_type_impl::native_type>(deserialized);
|
||||
|
||||
return make_ready_future<std::unordered_set<dht::token>>(decode_tokens(tokens));
|
||||
});
|
||||
|
||||
2
keys.hh
2
keys.hh
@@ -152,7 +152,7 @@ public:
|
||||
return TopLevel::from_bytes(get_compound_type(s)->serialize_optionals(v));
|
||||
}
|
||||
|
||||
static TopLevel from_deeply_exploded(const schema& s, const std::vector<boost::any>& v) {
|
||||
static TopLevel from_deeply_exploded(const schema& s, const std::vector<data_value>& v) {
|
||||
return TopLevel::from_bytes(get_compound_type(s)->serialize_value_deep(v));
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ void mutation::set_static_cell(const column_definition& def, atomic_cell_or_coll
|
||||
partition().static_row().apply(def, std::move(value));
|
||||
}
|
||||
|
||||
void mutation::set_static_cell(const bytes& name, const boost::any& value, api::timestamp_type timestamp, ttl_opt ttl) {
|
||||
void mutation::set_static_cell(const bytes& name, const data_value& value, api::timestamp_type timestamp, ttl_opt ttl) {
|
||||
auto column_def = schema()->get_column_definition(name);
|
||||
if (!column_def) {
|
||||
throw std::runtime_error(sprint("no column definition found for '%s'", name));
|
||||
@@ -65,7 +65,7 @@ void mutation::set_clustered_cell(const exploded_clustering_prefix& prefix, cons
|
||||
row.apply(def, std::move(value));
|
||||
}
|
||||
|
||||
void mutation::set_clustered_cell(const clustering_key& key, const bytes& name, const boost::any& value,
|
||||
void mutation::set_clustered_cell(const clustering_key& key, const bytes& name, const data_value& value,
|
||||
api::timestamp_type timestamp, ttl_opt ttl) {
|
||||
auto column_def = schema()->get_column_definition(name);
|
||||
if (!column_def) {
|
||||
@@ -79,7 +79,7 @@ void mutation::set_clustered_cell(const clustering_key& key, const column_defini
|
||||
row.apply(def, std::move(value));
|
||||
}
|
||||
|
||||
void mutation::set_cell(const exploded_clustering_prefix& prefix, const bytes& name, const boost::any& value,
|
||||
void mutation::set_cell(const exploded_clustering_prefix& prefix, const bytes& name, const data_value& value,
|
||||
api::timestamp_type timestamp, ttl_opt ttl) {
|
||||
auto column_def = schema()->get_column_definition(name);
|
||||
if (!column_def) {
|
||||
|
||||
@@ -64,11 +64,11 @@ public:
|
||||
mutation& operator=(mutation&& x) = default;
|
||||
|
||||
void set_static_cell(const column_definition& def, atomic_cell_or_collection&& value);
|
||||
void set_static_cell(const bytes& name, const boost::any& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_static_cell(const bytes& name, const data_value& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_clustered_cell(const exploded_clustering_prefix& prefix, const column_definition& def, atomic_cell_or_collection&& value);
|
||||
void set_clustered_cell(const clustering_key& key, const bytes& name, const boost::any& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_clustered_cell(const clustering_key& key, const bytes& name, const data_value& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_clustered_cell(const clustering_key& key, const column_definition& def, atomic_cell_or_collection&& value);
|
||||
void set_cell(const exploded_clustering_prefix& prefix, const bytes& name, const boost::any& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_cell(const exploded_clustering_prefix& prefix, const bytes& name, const data_value& value, api::timestamp_type timestamp, ttl_opt ttl = {});
|
||||
void set_cell(const exploded_clustering_prefix& prefix, const column_definition& def, atomic_cell_or_collection&& value);
|
||||
std::experimental::optional<atomic_cell_or_collection> get_cell(const clustering_key& rkey, const column_definition& def) const;
|
||||
const partition_key& key() const { return _ptr->_dk._key; };
|
||||
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
std::ostream& operator<<(std::ostream& out, const result_set_row& row) {
|
||||
for (auto&& cell : row._cells) {
|
||||
auto&& type = cell.second.type();
|
||||
auto&& value = cell.second.value();
|
||||
auto&& value = cell.second;
|
||||
out << cell.first << "=\"" << type->to_string(type->decompose(value)) << "\" ";
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
#include <experimental/optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/any.hpp>
|
||||
|
||||
namespace query {
|
||||
|
||||
class no_such_column : public std::runtime_error {
|
||||
@@ -70,11 +68,11 @@ public:
|
||||
template<typename T>
|
||||
std::experimental::optional<T>
|
||||
get(const sstring& column_name) const throw (no_such_column) {
|
||||
auto&& value = get_data_value(column_name).value();
|
||||
if (value.empty()) {
|
||||
auto&& value = get_data_value(column_name);
|
||||
if (value.is_null()) {
|
||||
return std::experimental::nullopt;
|
||||
}
|
||||
return std::experimental::optional<T>{boost::any_cast<T>(value)};
|
||||
return std::experimental::optional<T>{value_cast<T>(value)};
|
||||
}
|
||||
template<typename T>
|
||||
T get_nonnull(const sstring& column_name) const throw (no_such_column, null_column_value) {
|
||||
|
||||
@@ -53,11 +53,11 @@ public:
|
||||
_it++;
|
||||
}
|
||||
|
||||
inline size_t serialized_size(const boost::any& value) {
|
||||
inline size_t serialized_size(const data_value& value) {
|
||||
return (*_it)->serialized_size(value);
|
||||
}
|
||||
|
||||
inline void serialize(const boost::any& value, bytes::iterator& out) {
|
||||
inline void serialize(const data_value& value, bytes::iterator& out) {
|
||||
(*_it)->serialize(value, out);
|
||||
}
|
||||
};
|
||||
@@ -121,7 +121,7 @@ bytes from_components(Iterator begin, Iterator end, Serializer&& serializer, boo
|
||||
return b;
|
||||
}
|
||||
|
||||
key key::from_deeply_exploded(const schema& s, const std::vector<boost::any>& v) {
|
||||
key key::from_deeply_exploded(const schema& s, const std::vector<data_value>& v) {
|
||||
auto &pt = s.partition_key_type()->types();
|
||||
bool composite = pt.size() > 1;
|
||||
return from_components(v.begin(), v.end(), internal_serializer(pt), composite);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#pragma once
|
||||
#include "bytes.hh"
|
||||
#include "schema.hh"
|
||||
#include <boost/any.hpp>
|
||||
#include "core/future.hh"
|
||||
|
||||
class partition_key;
|
||||
@@ -91,7 +90,7 @@ public:
|
||||
key(bytes&& b) : _kind(kind::regular), _bytes(std::move(b)) {}
|
||||
key(kind k) : _kind(k) {}
|
||||
static key from_bytes(bytes b) { return key(std::move(b)); }
|
||||
static key from_deeply_exploded(const schema& s, const std::vector<boost::any>& v);
|
||||
static key from_deeply_exploded(const schema& s, const std::vector<data_value>& v);
|
||||
static key from_exploded(const schema& s, const std::vector<bytes>& v);
|
||||
static key from_exploded(const schema& s, std::vector<bytes>&& v);
|
||||
// Unfortunately, the _bytes field for the partition_key are not public. We can't move.
|
||||
|
||||
@@ -566,7 +566,7 @@ SEASTAR_TEST_CASE(test_partition_range_queries_with_bounds) {
|
||||
BOOST_REQUIRE(row[0]);
|
||||
BOOST_REQUIRE(row[1]);
|
||||
keys.push_back(*row[0]);
|
||||
tokens.push_back(boost::any_cast<int64_t>(long_type->deserialize(*row[1])));
|
||||
tokens.push_back(value_cast<int64_t>(long_type->deserialize(*row[1])));
|
||||
}
|
||||
BOOST_REQUIRE(keys.size() == 5);
|
||||
|
||||
@@ -705,35 +705,39 @@ SEASTAR_TEST_CASE(test_deletion_scenarios) {
|
||||
|
||||
SEASTAR_TEST_CASE(test_map_insert_update) {
|
||||
return do_with_cql_env([] (auto& e) {
|
||||
return e.create_table([](auto ks_name) {
|
||||
auto make_my_map_type = [] { return map_type_impl::get_instance(int32_type, int32_type, true); };
|
||||
auto my_map_type = make_my_map_type();
|
||||
return e.create_table([make_my_map_type] (auto ks_name) {
|
||||
// CQL: create table cf (p1 varchar primary key, map1 map<int, int>);
|
||||
auto my_map_type = map_type_impl::get_instance(int32_type, int32_type, true);
|
||||
return schema({}, ks_name, "cf",
|
||||
{{"p1", utf8_type}}, {}, {{"map1", my_map_type}}, {}, utf8_type);
|
||||
{{"p1", utf8_type}}, {}, {{"map1", make_my_map_type()}}, {}, utf8_type);
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, map1) values ('key1', { 1001: 2001 });").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{1001, 2001}}));
|
||||
"map1", make_map_value(my_map_type, map_type_impl::native_type({{1001, 2001}})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set map1[1002] = 2002 where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{1001, 2001},
|
||||
{1002, 2002}}));
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{1001, 2001},
|
||||
{1002, 2002}})));
|
||||
}).then([&e] {
|
||||
// overwrite an element
|
||||
return e.execute_cql("update cf set map1[1001] = 3001 where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{1001, 3001},
|
||||
{1002, 2002}}));
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{1001, 3001},
|
||||
{1002, 2002}})));
|
||||
}).then([&e] {
|
||||
// overwrite whole map
|
||||
return e.execute_cql("update cf set map1 = {1003: 4003} where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{1003, 4003}}));
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{1003, 4003}})));
|
||||
}).then([&e] {
|
||||
// overwrite whole map, but bad syntax
|
||||
return e.execute_cql("update cf set map1 = {1003, 4003} where p1 = 'key1';");
|
||||
@@ -744,25 +748,26 @@ SEASTAR_TEST_CASE(test_map_insert_update) {
|
||||
// overwrite whole map
|
||||
return e.execute_cql(
|
||||
"update cf set map1 = {1001: 5001, 1002: 5002, 1003: 5003} where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{1001, 5001},
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{1001, 5001},
|
||||
{1002, 5002},
|
||||
{1003, 5003}}));
|
||||
{1003, 5003}})));
|
||||
}).then([&e] {
|
||||
// discard some keys
|
||||
return e.execute_cql("update cf set map1 = map1 - {1001, 1003, 1005} where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{{1002, 5002}}}));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([](auto msg) {
|
||||
auto my_map_type = map_type_impl::get_instance(int32_type, int32_type, true);
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{{1002, 5002}}})));
|
||||
}).then([&e, my_map_type] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([my_map_type](auto msg) {
|
||||
assert_that(msg).is_rows()
|
||||
.with_size(1)
|
||||
.with_row({
|
||||
{utf8_type->decompose(sstring("key1"))},
|
||||
{my_map_type->decompose(map_type_impl::native_type{{{1002, 5002}}})},
|
||||
{my_map_type->decompose(make_map_value(my_map_type, map_type_impl::native_type{{{1002, 5002}}}))},
|
||||
});
|
||||
});
|
||||
}).then([&e] {
|
||||
@@ -771,133 +776,134 @@ SEASTAR_TEST_CASE(test_map_insert_update) {
|
||||
}).then([&e] {
|
||||
// delete a key
|
||||
return e.execute_cql("delete map1[1002] from cf where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({{{1009, 5009}}}));
|
||||
"map1", make_map_value(my_map_type,
|
||||
map_type_impl::native_type({{{1009, 5009}}})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, map1) values ('key1', null);").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_map_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"map1", map_type_impl::native_type({}));
|
||||
"map1", make_map_value(my_map_type, map_type_impl::native_type({})));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_set_insert_update) {
|
||||
return do_with_cql_env([] (auto&e) {
|
||||
return e.create_table([](auto ks_name) {
|
||||
auto make_my_set_type = [] { return set_type_impl::get_instance(int32_type, true); };
|
||||
auto my_set_type = make_my_set_type();
|
||||
return e.create_table([make_my_set_type](auto ks_name) {
|
||||
// CQL: create table cf (p1 varchar primary key, set1 set<int>);
|
||||
auto my_set_type = set_type_impl::get_instance(int32_type, true);
|
||||
return schema({}, ks_name, "cf",
|
||||
{{"p1", utf8_type}}, {}, {{"set1", my_set_type}}, {}, utf8_type);
|
||||
{{"p1", utf8_type}}, {}, {{"set1", make_my_set_type()}}, {}, utf8_type);
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, set1) values ('key1', { 1001 });").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1001}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1001})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set set1 = set1 + { 1002 } where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1001, 1002}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1001, 1002})));
|
||||
}).then([&e] {
|
||||
// overwrite an element
|
||||
return e.execute_cql("update cf set set1 = set1 + { 1001 } where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1001, 1002}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1001, 1002})));
|
||||
}).then([&e] {
|
||||
// overwrite entire set
|
||||
return e.execute_cql("update cf set set1 = { 1007, 1019 } where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1007, 1019}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1007, 1019})));
|
||||
}).then([&e] {
|
||||
// discard keys
|
||||
return e.execute_cql("update cf set set1 = set1 - { 1007, 1008 } where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1019}));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([](auto msg) {
|
||||
auto my_set_type = set_type_impl::get_instance(int32_type, true);
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1019})));
|
||||
}).then([&e, my_set_type] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([my_set_type](auto msg) {
|
||||
assert_that(msg).is_rows()
|
||||
.with_size(1)
|
||||
.with_row({
|
||||
{utf8_type->decompose(sstring("key1"))},
|
||||
{my_set_type->decompose(set_type_impl::native_type{{1019}})},
|
||||
{my_set_type->decompose(make_set_value(my_set_type, set_type_impl::native_type{{1019}}))},
|
||||
});
|
||||
});
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set set1 = set1 + { 1009 } where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
return e.execute_cql("delete set1[1019] from cf where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({1009}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({1009})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, set1) values ('key1', null);").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_set_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"set1", set_type_impl::native_type({}));
|
||||
"set1", make_set_value(my_set_type, set_type_impl::native_type({})));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_list_insert_update) {
|
||||
return do_with_cql_env([] (auto& e) {
|
||||
auto my_list_type = list_type_impl::get_instance(int32_type, true);
|
||||
return e.execute_cql("create table cf (p1 varchar primary key, list1 list<int>);").discard_result().then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, list1) values ('key1', [ 1001 ]);").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({boost::any(1001)}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({1001})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1 = [ 1002, 1003 ] where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({boost::any(1002), boost::any(1003)}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({1002, 1003})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1[1] = 2003 where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({boost::any(1002), boost::any(2003)}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({1002, 2003})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1 = list1 - [1002, 2004] where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({2003}));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([] (auto msg) {
|
||||
auto my_list_type = list_type_impl::get_instance(int32_type, true);
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({2003})));
|
||||
}).then([&e, my_list_type] {
|
||||
return e.execute_cql("select * from cf where p1 = 'key1';").then([my_list_type] (auto msg) {
|
||||
assert_that(msg).is_rows()
|
||||
.with_size(1)
|
||||
.with_row({
|
||||
{utf8_type->decompose(sstring("key1"))},
|
||||
{my_list_type->decompose(list_type_impl::native_type{{2003}})},
|
||||
{my_list_type->decompose(make_list_value(my_list_type, list_type_impl::native_type{{2003}}))},
|
||||
});
|
||||
});
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1 = [2008, 2009, 2010] where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
return e.execute_cql("delete list1[1] from cf where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({2008, 2010}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({2008, 2010})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1 = list1 + [2012, 2019] where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({2008, 2010, 2012, 2019}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({2008, 2010, 2012, 2019})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("update cf set list1 = [2001, 2002] + list1 where p1 = 'key1';").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({2001, 2002, 2008, 2010, 2012, 2019}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({2001, 2002, 2008, 2010, 2012, 2019})));
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, list1) values ('key1', null);").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.require_column_has_value("cf", {sstring("key1")}, {},
|
||||
"list1", list_type_impl::native_type({}));
|
||||
"list1", make_list_value(my_list_type, list_type_impl::native_type({})));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1028,7 +1034,7 @@ SEASTAR_TEST_CASE(test_tuples) {
|
||||
}).then([&e, tt] (shared_ptr<transport::messages::result_message> msg) {
|
||||
assert_that(msg).is_rows()
|
||||
.with_rows({{
|
||||
{tt->decompose(tuple_type_impl::native_type({int32_t(1001), int64_t(2001), sstring("abc1")}))},
|
||||
{tt->decompose(make_tuple_value(tt, tuple_type_impl::native_type({int32_t(1001), int64_t(2001), sstring("abc1")})))},
|
||||
}});
|
||||
return e.execute_cql("create table cf2 (p1 int PRIMARY KEY, r1 tuple<int, bigint, text>)").discard_result();
|
||||
}).then([&e] {
|
||||
@@ -1037,7 +1043,7 @@ SEASTAR_TEST_CASE(test_tuples) {
|
||||
return e.execute_cql("select * from cf2 where p1 = 1;");
|
||||
}).then([&e, tt] (auto msg) {
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{ int32_type->decompose(int32_t(1)), tt->decompose(tuple_type_impl::native_type({int32_t(1), int64_t(2), sstring("abc")})) }
|
||||
{ int32_type->decompose(int32_t(1)), tt->decompose(make_tuple_value(tt, tuple_type_impl::native_type({int32_t(1), int64_t(2), sstring("abc")}))) }
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1090,9 +1096,10 @@ SEASTAR_TEST_CASE(test_user_type) {
|
||||
return e.execute_cql("select t from cf where id = 2;");
|
||||
}).then([&e, make_user_type] (shared_ptr<transport::messages::result_message> msg) {
|
||||
auto ut = make_user_type();
|
||||
auto ut_val = user_type_impl::native_type({boost::any(int32_t(2001)),
|
||||
boost::any(int64_t(3001)),
|
||||
boost::any(sstring("abc4"))});
|
||||
auto ut_val = make_user_value(ut,
|
||||
user_type_impl::native_type({int32_t(2001),
|
||||
int64_t(3001),
|
||||
sstring("abc4")}));
|
||||
assert_that(msg).is_rows()
|
||||
.with_rows({
|
||||
{ut->decompose(ut_val)},
|
||||
@@ -1220,10 +1227,11 @@ SEASTAR_TEST_CASE(test_table_compression) {
|
||||
|
||||
SEASTAR_TEST_CASE(test_ttl) {
|
||||
return do_with_cql_env([] (cql_test_env& e) {
|
||||
return e.create_table([] (auto ks_name) {
|
||||
auto my_list_type = list_type_impl::get_instance(utf8_type, true);
|
||||
auto make_my_list_type = [] { return list_type_impl::get_instance(utf8_type, true); };
|
||||
auto my_list_type = make_my_list_type();
|
||||
return e.create_table([make_my_list_type] (auto ks_name) {
|
||||
return schema({}, ks_name, "cf",
|
||||
{{"p1", utf8_type}}, {}, {{"r1", utf8_type}, {"r2", utf8_type}, {"r3", my_list_type}}, {}, utf8_type);
|
||||
{{"p1", utf8_type}}, {}, {{"r1", utf8_type}, {"r2", utf8_type}, {"r3", make_my_list_type()}}, {}, utf8_type);
|
||||
}).then([&e] {
|
||||
return e.execute_cql(
|
||||
"update cf using ttl 1000 set r1 = 'value1_1', r3 = ['a', 'b', 'c'] where p1 = 'key1';").discard_result();
|
||||
@@ -1236,18 +1244,18 @@ SEASTAR_TEST_CASE(test_ttl) {
|
||||
return e.execute_cql("update cf using ttl 1 set r1 = 'value1_2' where p1 = 'key2';").discard_result();
|
||||
}).then([&e] {
|
||||
return e.execute_cql("insert into cf (p1, r2) values ('key2', 'value2_2');").discard_result();
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.execute_cql("select r1 from cf;").then([](auto msg) {
|
||||
assert_that(msg).is_rows().with_size(3)
|
||||
.with_row({utf8_type->decompose(sstring("value1_1"))})
|
||||
.with_row({utf8_type->decompose(sstring("value1_2"))})
|
||||
.with_row({utf8_type->decompose(sstring("value1_3"))});
|
||||
});
|
||||
}).then([&e] {
|
||||
return e.execute_cql("select r3 from cf where p1 = 'key1';").then([] (auto msg) {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.execute_cql("select r3 from cf where p1 = 'key1';").then([my_list_type] (auto msg) {
|
||||
auto my_list_type = list_type_impl::get_instance(utf8_type, true);
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{my_list_type->decompose(list_type_impl::native_type{{sstring("a"), sstring("b"), sstring("c")}})}
|
||||
{my_list_type->decompose(make_list_value(my_list_type, list_type_impl::native_type{{sstring("a"), sstring("b"), sstring("c")}}))}
|
||||
});
|
||||
});
|
||||
}).then([&e] {
|
||||
@@ -1269,11 +1277,11 @@ SEASTAR_TEST_CASE(test_ttl) {
|
||||
.with_row({ {} })
|
||||
.with_row({ utf8_type->decompose(sstring("value1_1")) });
|
||||
});
|
||||
}).then([&e] {
|
||||
}).then([&e, my_list_type] {
|
||||
return e.execute_cql("select r3 from cf where p1 = 'key1';").then([] (auto msg) {
|
||||
auto my_list_type = list_type_impl::get_instance(utf8_type, true);
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{my_list_type->decompose(list_type_impl::native_type{{sstring("a"), sstring("c")}})}
|
||||
{my_list_type->decompose(make_list_value(my_list_type, list_type_impl::native_type{{sstring("a"), sstring("c")}}))}
|
||||
});
|
||||
});
|
||||
}).then([&e] {
|
||||
|
||||
@@ -212,27 +212,29 @@ public:
|
||||
}
|
||||
|
||||
virtual future<> require_column_has_value(const sstring& table_name,
|
||||
std::vector<boost::any> pk,
|
||||
std::vector<boost::any> ck,
|
||||
std::vector<data_value> pk,
|
||||
std::vector<data_value> ck,
|
||||
const sstring& column_name,
|
||||
boost::any expected) override {
|
||||
data_value expected) override {
|
||||
auto& db = _db->local();
|
||||
auto& cf = db.find_column_family(ks_name, table_name);
|
||||
auto schema = cf.schema();
|
||||
auto pkey = partition_key::from_deeply_exploded(*schema, pk);
|
||||
auto ckey = clustering_key::from_deeply_exploded(*schema, ck);
|
||||
auto exp = expected.type()->decompose(expected);
|
||||
auto dk = dht::global_partitioner().decorate_key(*schema, pkey);
|
||||
auto shard = db.shard_of(dk._token);
|
||||
return _db->invoke_on(shard, [pkey = std::move(pkey),
|
||||
ck = std::move(ck),
|
||||
ckey = std::move(ckey),
|
||||
ks_name = std::move(ks_name),
|
||||
column_name = std::move(column_name),
|
||||
expected = std::move(expected),
|
||||
exp = std::move(exp),
|
||||
table_name = std::move(table_name)] (database& db) mutable {
|
||||
auto& cf = db.find_column_family(ks_name, table_name);
|
||||
auto schema = cf.schema();
|
||||
return cf.find_partition_slow(pkey).then([schema, ck, column_name, expected] (column_family::const_mutation_partition_ptr p) {
|
||||
return cf.find_partition_slow(pkey).then([schema, ckey, column_name, exp] (column_family::const_mutation_partition_ptr p) {
|
||||
assert(p != nullptr);
|
||||
auto row = p->find_row(clustering_key::from_deeply_exploded(*schema, ck));
|
||||
auto row = p->find_row(ckey);
|
||||
assert(row != nullptr);
|
||||
auto col_def = schema->get_column_definition(utf8_type->decompose(column_name));
|
||||
assert(col_def != nullptr);
|
||||
@@ -251,7 +253,7 @@ public:
|
||||
actual = type->to_value(type->deserialize_mutation_form(c),
|
||||
serialization_format::internal());
|
||||
}
|
||||
assert(col_def->type->equal(actual, col_def->type->decompose(expected)));
|
||||
assert(col_def->type->equal(actual, exp));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ public:
|
||||
|
||||
virtual future<> require_column_has_value(
|
||||
const sstring& table_name,
|
||||
std::vector<boost::any> pk,
|
||||
std::vector<boost::any> ck,
|
||||
std::vector<data_value> pk,
|
||||
std::vector<data_value> ck,
|
||||
const sstring& column_name,
|
||||
boost::any expected) = 0;
|
||||
data_value expected) = 0;
|
||||
|
||||
virtual future<> stop() = 0;
|
||||
|
||||
|
||||
@@ -106,10 +106,10 @@ SEASTAR_TEST_CASE(test_multi_level_row_tombstones) {
|
||||
|
||||
mutation m(partition_key::from_exploded(*s, {to_bytes("key1")}), s);
|
||||
|
||||
auto make_prefix = [s] (const std::vector<boost::any>& v) {
|
||||
auto make_prefix = [s] (const std::vector<data_value>& v) {
|
||||
return clustering_key_prefix::from_deeply_exploded(*s, v);
|
||||
};
|
||||
auto make_key = [s] (const std::vector<boost::any>& v) {
|
||||
auto make_key = [s] (const std::vector<data_value>& v) {
|
||||
return clustering_key::from_deeply_exploded(*s, v);
|
||||
};
|
||||
|
||||
@@ -423,12 +423,12 @@ SEASTAR_TEST_CASE(test_multiple_memtables_multiple_partitions) {
|
||||
|
||||
return do_with(std::move(result), [&cf, s, &r1_col, shadow] (auto& result) {
|
||||
return cf.for_all_partitions_slow([&, s] (const dht::decorated_key& pk, const mutation_partition& mp) {
|
||||
auto p1 = boost::any_cast<int32_t>(int32_type->deserialize(pk._key.explode(*s)[0]));
|
||||
auto p1 = value_cast<int32_t>(int32_type->deserialize(pk._key.explode(*s)[0]));
|
||||
for (const rows_entry& re : mp.range(*s, query::range<clustering_key_prefix>())) {
|
||||
auto c1 = boost::any_cast<int32_t>(int32_type->deserialize(re.key().explode(*s)[0]));
|
||||
auto c1 = value_cast<int32_t>(int32_type->deserialize(re.key().explode(*s)[0]));
|
||||
auto cell = re.row().cells().find_cell(r1_col.id);
|
||||
if (cell) {
|
||||
result[p1][c1] = boost::any_cast<int32_t>(int32_type->deserialize(cell->as_atomic_cell().value()));
|
||||
result[p1][c1] = value_cast<int32_t>(int32_type->deserialize(cell->as_atomic_cell().value()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -601,7 +601,7 @@ SEASTAR_TEST_CASE(test_partition_with_live_data_in_static_row_is_present_in_the_
|
||||
assert_that(query::result_set::from_raw_result(s, slice, m.query(slice)))
|
||||
.has_only(a_row()
|
||||
.with_column("pk", bytes("key1"))
|
||||
.with_column("v", {}));
|
||||
.with_column("v", data_value::make_null(bytes_type)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@ SEASTAR_TEST_CASE(test_query_result_with_one_regular_column_missing) {
|
||||
.with_column("pk", bytes("key1"))
|
||||
.with_column("ck", bytes("ck:A"))
|
||||
.with_column("v1", bytes("v1:value"))
|
||||
.with_column("v2", {}));
|
||||
.with_column("v2", data_value::make_null(bytes_type)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
|
||||
void fill_memtable() {
|
||||
for (unsigned i = 0; i < _cfg.partitions; i++) {
|
||||
auto key = partition_key::from_deeply_exploded(*s, { boost::any(random_key()) });
|
||||
auto key = partition_key::from_deeply_exploded(*s, { random_key() });
|
||||
auto mut = mutation(key, s);
|
||||
for (auto& cdef: s->regular_columns()) {
|
||||
mut.set_clustered_cell(clustering_key::make_empty(*s), cdef, atomic_cell::make_live(0, utf8_type->decompose(random_column())));
|
||||
|
||||
@@ -39,12 +39,12 @@ row_assertion::matches(const query::result_set_row& row) const {
|
||||
auto ss_name = to_sstring(name);
|
||||
|
||||
if (!row.has(ss_name)) {
|
||||
if (!value.empty()) {
|
||||
if (!value.is_null()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const data_value& val = row.get_data_value(ss_name);
|
||||
if (val != data_value(boost::any(value), val.type())) {
|
||||
if (val != value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ row_assertion::describe(schema_ptr schema) const {
|
||||
if (!def) {
|
||||
BOOST_FAIL(sprint("Schema is missing column definition for '%s'", name));
|
||||
}
|
||||
if (value.empty()) {
|
||||
if (value.is_null()) {
|
||||
return sprint("%s=null", to_sstring(name));
|
||||
} else {
|
||||
return sprint("%s=\"%s\"", to_sstring(name), def->type->to_string(def->type->decompose(value)));
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
//
|
||||
|
||||
class row_assertion {
|
||||
std::map<bytes, boost::any> _expected_values;
|
||||
std::map<bytes, data_value> _expected_values;
|
||||
public:
|
||||
row_assertion& with_column(bytes name, boost::any value) {
|
||||
row_assertion& with_column(bytes name, data_value value) {
|
||||
_expected_values.emplace(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -919,7 +919,7 @@ SEASTAR_TEST_CASE(datafile_generation_16) {
|
||||
mutation m(key, s);
|
||||
|
||||
auto c_key = clustering_key::make_empty(*s);
|
||||
m.set_clustered_cell(c_key, to_bytes("col2"), boost::any(i), api::max_timestamp);
|
||||
m.set_clustered_cell(c_key, to_bytes("col2"), i, api::max_timestamp);
|
||||
mtp->apply(std::move(m));
|
||||
}
|
||||
|
||||
@@ -1340,7 +1340,7 @@ SEASTAR_TEST_CASE(datafile_generation_37) {
|
||||
auto clustering = clustering_key::from_clustering_prefix(*s, exploded);
|
||||
|
||||
auto row = mp.clustered_row(clustering);
|
||||
match_live_cell(row.cells(), *s, "cl2", boost::any(to_bytes("cl2")));
|
||||
match_live_cell(row.cells(), *s, "cl2", to_bytes("cl2"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
@@ -1375,7 +1375,7 @@ SEASTAR_TEST_CASE(datafile_generation_38) {
|
||||
auto clustering = clustering_key::from_clustering_prefix(*s, exploded);
|
||||
|
||||
auto row = mp.clustered_row(clustering);
|
||||
match_live_cell(row.cells(), *s, "cl3", boost::any(to_bytes("cl3")));
|
||||
match_live_cell(row.cells(), *s, "cl3", to_bytes("cl3"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
@@ -1408,8 +1408,8 @@ SEASTAR_TEST_CASE(datafile_generation_39) {
|
||||
return sstp->read_row(s, key).then([sstp, s] (auto mutation) {
|
||||
auto& mp = mutation->partition();
|
||||
auto row = mp.clustered_row(clustering_key::make_empty(*s));
|
||||
match_live_cell(row.cells(), *s, "cl1", boost::any(to_bytes("cl1")));
|
||||
match_live_cell(row.cells(), *s, "cl2", boost::any(to_bytes("cl2")));
|
||||
match_live_cell(row.cells(), *s, "cl1", to_bytes("cl1"));
|
||||
match_live_cell(row.cells(), *s, "cl2", to_bytes("cl2"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ SEASTAR_TEST_CASE(nonexistent_key) {
|
||||
});
|
||||
}
|
||||
|
||||
future<> test_no_clustered(bytes&& key, std::unordered_map<bytes, boost::any> &&map) {
|
||||
future<> test_no_clustered(bytes&& key, std::unordered_map<bytes, data_value> &&map) {
|
||||
return reusable_sst("tests/sstables/uncompressed", 1).then([k = std::move(key), map = std::move(map)] (auto sstp) mutable {
|
||||
return do_with(sstables::key(std::move(k)), [sstp, map = std::move(map)] (auto& key) {
|
||||
auto s = uncompressed_schema();
|
||||
@@ -72,19 +72,19 @@ future<> test_no_clustered(bytes&& key, std::unordered_map<bytes, boost::any> &&
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(uncompressed_1) {
|
||||
return test_no_clustered("vinna", {{ "col1", boost::any(to_sstring("daughter")) }, { "col2", boost::any(3) }});
|
||||
return test_no_clustered("vinna", {{ "col1", to_sstring("daughter") }, { "col2", 3 }});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(uncompressed_2) {
|
||||
return test_no_clustered("gustaf", {{ "col1", boost::any(to_sstring("son")) }, { "col2", boost::any(0) }});
|
||||
return test_no_clustered("gustaf", {{ "col1", to_sstring("son") }, { "col2", 0 }});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(uncompressed_3) {
|
||||
return test_no_clustered("isak", {{ "col1", boost::any(to_sstring("son")) }, { "col2", boost::any(1) }});
|
||||
return test_no_clustered("isak", {{ "col1", to_sstring("son") }, { "col2", 1 }});
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(uncompressed_4) {
|
||||
return test_no_clustered("finna", {{ "col1", boost::any(to_sstring("daughter")) }, { "col2", boost::any(2) }});
|
||||
return test_no_clustered("finna", {{ "col1", to_sstring("daughter") }, { "col2", 2 }});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -138,10 +138,10 @@ SEASTAR_TEST_CASE(complex_sst1_k1) {
|
||||
auto s = complex_schema();
|
||||
|
||||
auto sr = mutation.partition().static_row();
|
||||
match_live_cell(sr, *s, "static_obj", boost::any(to_bytes("static_value")));
|
||||
match_live_cell(sr, *s, "static_obj", to_bytes("static_value"));
|
||||
|
||||
auto row1 = clustered_row(mutation, *s, {"cl1.1", "cl2.1"});
|
||||
match_live_cell(row1.cells(), *s, "reg", boost::any(to_bytes("v1")));
|
||||
match_live_cell(row1.cells(), *s, "reg", to_bytes("v1"));
|
||||
match_absent(row1.cells(), *s, "reg_list");
|
||||
match_absent(row1.cells(), *s, "reg_map");
|
||||
match_absent(row1.cells(), *s, "reg_fset");
|
||||
@@ -150,7 +150,7 @@ SEASTAR_TEST_CASE(complex_sst1_k1) {
|
||||
match_collection_element<status::live>(reg_set.cells[1], to_bytes("2"), bytes_opt{});
|
||||
|
||||
auto row2 = clustered_row(mutation, *s, {"cl1.2", "cl2.2"});
|
||||
match_live_cell(row2.cells(), *s, "reg", boost::any(to_bytes("v2")));
|
||||
match_live_cell(row2.cells(), *s, "reg", to_bytes("v2"));
|
||||
match_absent(row2.cells(), *s, "reg_set");
|
||||
match_absent(row2.cells(), *s, "reg_map");
|
||||
match_absent(row2.cells(), *s, "reg_fset");
|
||||
@@ -167,7 +167,7 @@ SEASTAR_TEST_CASE(complex_sst1_k2) {
|
||||
auto s = complex_schema();
|
||||
|
||||
auto sr = mutation.partition().static_row();
|
||||
match_live_cell(sr, *s, "static_obj", boost::any(to_bytes("static_value")));
|
||||
match_live_cell(sr, *s, "static_obj", to_bytes("static_value"));
|
||||
auto static_set = match_collection(sr, *s, "static_collection", tombstone(deletion_time{1431451390, 1431451390225257l}));
|
||||
match_collection_element<status::live>(static_set.cells[0], to_bytes("1"), bytes_opt{});
|
||||
match_collection_element<status::live>(static_set.cells[1], to_bytes("2"), bytes_opt{});
|
||||
@@ -175,7 +175,7 @@ SEASTAR_TEST_CASE(complex_sst1_k2) {
|
||||
match_collection_element<status::live>(static_set.cells[3], to_bytes("4"), bytes_opt{});
|
||||
|
||||
auto row1 = clustered_row(mutation, *s, {"kcl1.1", "kcl2.1"});
|
||||
match_live_cell(row1.cells(), *s, "reg", boost::any(to_bytes("v3")));
|
||||
match_live_cell(row1.cells(), *s, "reg", to_bytes("v3"));
|
||||
match_absent(row1.cells(), *s, "reg_list");
|
||||
match_absent(row1.cells(), *s, "reg_set");
|
||||
match_absent(row1.cells(), *s, "reg_fset");
|
||||
@@ -184,7 +184,7 @@ SEASTAR_TEST_CASE(complex_sst1_k2) {
|
||||
match_collection_element<status::live>(reg_map.cells[1], to_bytes("4"), to_bytes("2"));
|
||||
|
||||
auto row2 = clustered_row(mutation, *s, {"kcl1.2", "kcl2.2"});
|
||||
match_live_cell(row2.cells(), *s, "reg", boost::any(to_bytes("v4")));
|
||||
match_live_cell(row2.cells(), *s, "reg", to_bytes("v4"));
|
||||
match_absent(row2.cells(), *s, "reg_set");
|
||||
match_absent(row2.cells(), *s, "reg_map");
|
||||
match_absent(row2.cells(), *s, "reg_list");
|
||||
@@ -245,11 +245,11 @@ SEASTAR_TEST_CASE(complex_sst2_k3) {
|
||||
auto s = complex_schema();
|
||||
|
||||
auto sr = mutation.partition().static_row();
|
||||
match_expiring_cell(sr, *s, "static_obj", boost::any(to_bytes("static_value_3")), 1431451394597062l, 1431537794);
|
||||
match_expiring_cell(sr, *s, "static_obj", to_bytes("static_value_3"), 1431451394597062l, 1431537794);
|
||||
|
||||
auto row1 = clustered_row(mutation, *s, {"tcl1.1", "tcl2.1"});
|
||||
BOOST_REQUIRE(row1.created_at() == 1431451394597062l);
|
||||
match_expiring_cell(row1.cells(), *s, "reg", boost::any(to_bytes("v5")), 1431451394597062l, 1431537794);
|
||||
match_expiring_cell(row1.cells(), *s, "reg", to_bytes("v5"), 1431451394597062l, 1431537794);
|
||||
match_absent(row1.cells(), *s, "reg_list");
|
||||
match_absent(row1.cells(), *s, "reg_set");
|
||||
match_absent(row1.cells(), *s, "reg_map");
|
||||
@@ -283,7 +283,7 @@ SEASTAR_TEST_CASE(complex_sst3_k2) {
|
||||
auto s = complex_schema();
|
||||
|
||||
auto sr = mutation.partition().static_row();
|
||||
match_live_cell(sr, *s, "static_obj", boost::any(to_bytes("final_static")));
|
||||
match_live_cell(sr, *s, "static_obj", to_bytes("final_static"));
|
||||
|
||||
auto row = clustered_row(mutation, *s, {"kcl1.1", "kcl2.1"});
|
||||
auto reg_map = match_collection(row.cells(), *s, "reg_map", tombstone(deletion_time{0, api::missing_timestamp}));
|
||||
@@ -385,8 +385,8 @@ SEASTAR_TEST_CASE(compact_storage_sparse_read) {
|
||||
return sstp->read_row(s, key).then([sstp, s, &key] (auto mutation) {
|
||||
auto& mp = mutation->partition();
|
||||
auto row = mp.clustered_row(clustering_key::make_empty(*s));
|
||||
match_live_cell(row.cells(), *s, "cl1", boost::any(to_bytes("cl1")));
|
||||
match_live_cell(row.cells(), *s, "cl2", boost::any(to_bytes("cl2")));
|
||||
match_live_cell(row.cells(), *s, "cl1", to_bytes("cl1"));
|
||||
match_live_cell(row.cells(), *s, "cl2", to_bytes("cl2"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
@@ -404,7 +404,7 @@ SEASTAR_TEST_CASE(compact_storage_simple_dense_read) {
|
||||
auto clustering = clustering_key::from_clustering_prefix(*s, exploded);
|
||||
|
||||
auto row = mp.clustered_row(clustering);
|
||||
match_live_cell(row.cells(), *s, "cl2", boost::any(to_bytes("cl2")));
|
||||
match_live_cell(row.cells(), *s, "cl2", to_bytes("cl2"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
@@ -422,7 +422,7 @@ SEASTAR_TEST_CASE(compact_storage_dense_read) {
|
||||
auto clustering = clustering_key::from_clustering_prefix(*s, exploded);
|
||||
|
||||
auto row = mp.clustered_row(clustering);
|
||||
match_live_cell(row.cells(), *s, "cl3", boost::any(to_bytes("cl3")));
|
||||
match_live_cell(row.cells(), *s, "cl3", to_bytes("cl3"));
|
||||
return make_ready_future<>();
|
||||
});
|
||||
});
|
||||
@@ -452,7 +452,7 @@ SEASTAR_TEST_CASE(broken_ranges_collection) {
|
||||
return repeat([s, reader] {
|
||||
return (*reader)().then([s, reader] (mutation_opt mut) {
|
||||
auto key_equal = [s, &mut] (sstring ip) {
|
||||
return mut->key().representation() == partition_key::from_deeply_exploded(*s, { boost::any(net::ipv4_address(ip)) }).representation();
|
||||
return mut->key().representation() == partition_key::from_deeply_exploded(*s, { net::ipv4_address(ip) }).representation();
|
||||
};
|
||||
|
||||
if (!mut) {
|
||||
|
||||
@@ -669,16 +669,17 @@ SEASTAR_TEST_CASE(find_key_map) {
|
||||
return reusable_sst("tests/sstables/map_pk", 1).then([] (auto sstp) {
|
||||
schema_ptr s = map_schema();
|
||||
auto& summary = sstables::test(sstp)._summary();
|
||||
std::vector<boost::any> kk;
|
||||
std::vector<data_value> kk;
|
||||
|
||||
auto b1 = to_bytes("2");
|
||||
auto b2 = to_bytes("2");
|
||||
|
||||
auto map_element = std::make_pair<boost::any, boost::any>(boost::any(b1), boost::any(b2));
|
||||
std::vector<std::pair<boost::any, boost::any>> map;
|
||||
auto map_type = map_type_impl::get_instance(bytes_type, bytes_type, true);
|
||||
auto map_element = std::make_pair<data_value, data_value>(b1, b2);
|
||||
std::vector<std::pair<data_value, data_value>> map;
|
||||
map.push_back(map_element);
|
||||
|
||||
kk.push_back(map);
|
||||
kk.push_back(make_map_value(map_type, map));
|
||||
|
||||
auto key = sstables::key::from_deeply_exploded(*s, kk);
|
||||
BOOST_REQUIRE(sstables::test(sstp).binary_search(summary.entries, key) == 0);
|
||||
@@ -689,16 +690,17 @@ SEASTAR_TEST_CASE(find_key_set) {
|
||||
return reusable_sst("tests/sstables/set_pk", 1).then([] (auto sstp) {
|
||||
schema_ptr s = set_schema();
|
||||
auto& summary = sstables::test(sstp)._summary();
|
||||
std::vector<boost::any> kk;
|
||||
std::vector<data_value> kk;
|
||||
|
||||
std::vector<boost::any> set;
|
||||
std::vector<data_value> set;
|
||||
|
||||
bytes b1("1");
|
||||
bytes b2("2");
|
||||
|
||||
set.push_back(boost::any(b1));
|
||||
set.push_back(boost::any(b2));
|
||||
kk.push_back(set);
|
||||
set.push_back(b1);
|
||||
set.push_back(b2);
|
||||
auto set_type = set_type_impl::get_instance(bytes_type, true);
|
||||
kk.push_back(make_set_value(set_type, set));
|
||||
|
||||
auto key = sstables::key::from_deeply_exploded(*s, kk);
|
||||
BOOST_REQUIRE(sstables::test(sstp).binary_search(summary.entries, key) == 0);
|
||||
@@ -709,16 +711,17 @@ SEASTAR_TEST_CASE(find_key_list) {
|
||||
return reusable_sst("tests/sstables/list_pk", 1).then([] (auto sstp) {
|
||||
schema_ptr s = set_schema();
|
||||
auto& summary = sstables::test(sstp)._summary();
|
||||
std::vector<boost::any> kk;
|
||||
std::vector<data_value> kk;
|
||||
|
||||
std::vector<boost::any> list;
|
||||
std::vector<data_value> list;
|
||||
|
||||
bytes b1("1");
|
||||
bytes b2("2");
|
||||
list.push_back(boost::any(b1));
|
||||
list.push_back(boost::any(b2));
|
||||
list.push_back(b1);
|
||||
list.push_back(b2);
|
||||
|
||||
kk.push_back(list);
|
||||
auto list_type = list_type_impl::get_instance(bytes_type, true);
|
||||
kk.push_back(make_list_value(list_type, list));
|
||||
|
||||
auto key = sstables::key::from_deeply_exploded(*s, kk);
|
||||
BOOST_REQUIRE(sstables::test(sstp).binary_search(summary.entries, key) == 0);
|
||||
@@ -730,13 +733,13 @@ SEASTAR_TEST_CASE(find_key_composite) {
|
||||
return reusable_sst("tests/sstables/composite", 1).then([] (auto sstp) {
|
||||
schema_ptr s = composite_schema();
|
||||
auto& summary = sstables::test(sstp)._summary();
|
||||
std::vector<boost::any> kk;
|
||||
std::vector<data_value> kk;
|
||||
|
||||
auto b1 = bytes("HCG8Ee7ENWqfCXipk4-Ygi2hzrbfHC8pTtH3tEmV3d9p2w8gJPuMN_-wp1ejLRf4kNEPEgtgdHXa6NoFE7qUig==");
|
||||
auto b2 = bytes("VJizqYxC35YpLaPEJNt_4vhbmKJxAg54xbiF1UkL_9KQkqghVvq34rZ6Lm8eRTi7JNJCXcH6-WtNUSFJXCOfdg==");
|
||||
|
||||
kk.push_back(boost::any(b1));
|
||||
kk.push_back(boost::any(b2));
|
||||
kk.push_back(b1);
|
||||
kk.push_back(b2);
|
||||
|
||||
auto key = sstables::key::from_deeply_exploded(*s, kk);
|
||||
BOOST_REQUIRE(sstables::test(sstp).binary_search(summary.entries, key) == 0);
|
||||
@@ -771,13 +774,13 @@ SEASTAR_TEST_CASE(not_find_key_composite_bucket0) {
|
||||
return reusable_sst("tests/sstables/composite", 1).then([] (auto sstp) {
|
||||
schema_ptr s = composite_schema();
|
||||
auto& summary = sstables::test(sstp)._summary();
|
||||
std::vector<boost::any> kk;
|
||||
std::vector<data_value> kk;
|
||||
|
||||
auto b1 = bytes("ZEunFCoqAidHOrPiU3U6UAvUU01IYGvT3kYtYItJ1ODTk7FOsEAD-dqmzmFNfTDYvngzkZwKrLxthB7ItLZ4HQ==");
|
||||
auto b2 = bytes("K-GpWx-QtyzLb12z5oNS0C03d3OzNyBKdYJh1XjHiC53KudoqdoFutHUMFLe6H9Emqv_fhwIJEKEb5Csn72f9A==");
|
||||
|
||||
kk.push_back(boost::any(b1));
|
||||
kk.push_back(boost::any(b2));
|
||||
kk.push_back(b1);
|
||||
kk.push_back(b2);
|
||||
|
||||
auto key = sstables::key::from_deeply_exploded(*s, kk);
|
||||
// (result + 1) * -1 -1 = 0
|
||||
|
||||
@@ -436,7 +436,7 @@ inline bool check_status_and_done(const atomic_cell &c, status expected) {
|
||||
}
|
||||
|
||||
template <status Status>
|
||||
inline void match(const row& row, const schema& s, bytes col, const boost::any& value, int64_t timestamp = 0, int32_t expiration = 0) {
|
||||
inline void match(const row& row, const schema& s, bytes col, const data_value& value, int64_t timestamp = 0, int32_t expiration = 0) {
|
||||
auto cdef = s.get_column_definition(col);
|
||||
|
||||
BOOST_CHECK_NO_THROW(row.cell_at(cdef->id));
|
||||
@@ -455,16 +455,16 @@ inline void match(const row& row, const schema& s, bytes col, const boost::any&
|
||||
}
|
||||
}
|
||||
|
||||
inline void match_live_cell(const row& row, const schema& s, bytes col, const boost::any& value) {
|
||||
inline void match_live_cell(const row& row, const schema& s, bytes col, const data_value& value) {
|
||||
match<status::live>(row, s, col, value);
|
||||
}
|
||||
|
||||
inline void match_expiring_cell(const row& row, const schema& s, bytes col, const boost::any& value, int64_t timestamp, int32_t expiration) {
|
||||
inline void match_expiring_cell(const row& row, const schema& s, bytes col, const data_value& value, int64_t timestamp, int32_t expiration) {
|
||||
match<status::ttl>(row, s, col, value);
|
||||
}
|
||||
|
||||
inline void match_dead_cell(const row& row, const schema& s, bytes col) {
|
||||
match<status::dead>(row, s, col, boost::any({}));
|
||||
match<status::dead>(row, s, col, 0); // value will be ignored
|
||||
}
|
||||
|
||||
inline void match_absent(const row& row, const schema& s, bytes col) {
|
||||
|
||||
@@ -212,18 +212,18 @@ BOOST_AUTO_TEST_CASE(test_varint) {
|
||||
BOOST_REQUIRE(varint_type->equal(varint_type->from_string("1"), varint_type->decompose(boost::multiprecision::cpp_int(1))));
|
||||
BOOST_REQUIRE(varint_type->equal(varint_type->from_string("0"), varint_type->decompose(boost::multiprecision::cpp_int(0))));
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("-1"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("-1"))),
|
||||
boost::multiprecision::cpp_int(-1));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("255"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("255"))),
|
||||
boost::multiprecision::cpp_int(255));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("1"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("1"))),
|
||||
boost::multiprecision::cpp_int(1));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("0"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("0"))),
|
||||
boost::multiprecision::cpp_int(0));
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("-123"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("-123"))),
|
||||
boost::multiprecision::cpp_int(-123));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("123"))),
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(varint_type->from_string("123"))),
|
||||
boost::multiprecision::cpp_int(123));
|
||||
|
||||
BOOST_REQUIRE(varint_type->equal(from_hex("000000"), from_hex("00")));
|
||||
@@ -235,8 +235,8 @@ BOOST_AUTO_TEST_CASE(test_varint) {
|
||||
|
||||
BOOST_REQUIRE(varint_type->equal(from_hex("00ff"), varint_type->decompose(boost::multiprecision::cpp_int(255))));
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("ff"))), boost::multiprecision::cpp_int(-1));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00ff"))), boost::multiprecision::cpp_int(255));
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("ff"))), boost::multiprecision::cpp_int(-1));
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00ff"))), boost::multiprecision::cpp_int(255));
|
||||
|
||||
BOOST_REQUIRE(!varint_type->equal(from_hex("00ff"), varint_type->decompose(boost::multiprecision::cpp_int(-1))));
|
||||
BOOST_REQUIRE(!varint_type->equal(from_hex("ff"), varint_type->decompose(boost::multiprecision::cpp_int(255))));
|
||||
@@ -244,49 +244,49 @@ BOOST_AUTO_TEST_CASE(test_varint) {
|
||||
BOOST_REQUIRE(varint_type->equal(from_hex("00deadbeef"), varint_type->decompose(boost::multiprecision::cpp_int("0xdeadbeef"))));
|
||||
BOOST_REQUIRE(varint_type->equal(from_hex("00ffffffffffffffffffffffffffffffff"), varint_type->decompose(boost::multiprecision::cpp_int("340282366920938463463374607431768211455"))));
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00deadbeef"))), boost::multiprecision::cpp_int("0xdeadbeef"));
|
||||
BOOST_CHECK_EQUAL(boost::any_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00ffffffffffffffffffffffffffffffff"))), boost::multiprecision::cpp_int("340282366920938463463374607431768211455"));
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00deadbeef"))), boost::multiprecision::cpp_int("0xdeadbeef"));
|
||||
BOOST_CHECK_EQUAL(value_cast<boost::multiprecision::cpp_int>(varint_type->deserialize(from_hex("00ffffffffffffffffffffffffffffffff"))), boost::multiprecision::cpp_int("340282366920938463463374607431768211455"));
|
||||
|
||||
test_parsing_fails(varint_type, "1A");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_decimal) {
|
||||
auto bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("-1")));
|
||||
auto bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("-1")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 0);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), -1);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "-1");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("-1.00")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("-1.00")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 2);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), -100);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "-1");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("123")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("123")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 0);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "123");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23e3")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23e3")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), -1);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "1230");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23e+3")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23e+3")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), -1);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "1230");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("1.23")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 2);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "1.23");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("0.123")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("0.123")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 3);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "0.123");
|
||||
|
||||
bd = boost::any_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("0.00123")));
|
||||
bd = value_cast<big_decimal>(decimal_type->deserialize(decimal_type->from_string("0.00123")));
|
||||
BOOST_CHECK_EQUAL(bd.scale(), 5);
|
||||
BOOST_CHECK_EQUAL(bd.unscaled_value(), 123);
|
||||
BOOST_CHECK_EQUAL(decimal_type->to_string(decimal_type->decompose(bd)), "0.00123");
|
||||
@@ -328,21 +328,11 @@ BOOST_AUTO_TEST_CASE(test_compound_type_compare) {
|
||||
|
||||
template <typename T>
|
||||
std::experimental::optional<T>
|
||||
extract(boost::any a) {
|
||||
if (a.empty()) {
|
||||
extract(data_value a) {
|
||||
if (a.is_null()) {
|
||||
return std::experimental::nullopt;
|
||||
} else {
|
||||
return std::experimental::make_optional(boost::any_cast<T>(a));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
boost::any
|
||||
unextract(std::experimental::optional<T> v) {
|
||||
if (v) {
|
||||
return boost::any(*v);
|
||||
} else {
|
||||
return boost::any();
|
||||
return std::experimental::make_optional(value_cast<T>(a));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,13 +347,13 @@ BOOST_AUTO_TEST_CASE(test_tuple) {
|
||||
return std::make_tuple(extract<int32_t>(v[0]), extract<int64_t>(v[1]), extract<sstring>(v[2]));
|
||||
};
|
||||
auto c_to_native = [] (std::tuple<opt<int32_t>, opt<int64_t>, opt<sstring>> v) {
|
||||
return native_type({unextract(std::get<0>(v)), unextract(std::get<1>(v)), unextract(std::get<2>(v))});
|
||||
return native_type({std::get<0>(v), std::get<1>(v), std::get<2>(v)});
|
||||
};
|
||||
auto native_to_bytes = [t] (native_type v) {
|
||||
return t->decompose(v);
|
||||
return t->decompose(make_tuple_value(t, v));
|
||||
};
|
||||
auto bytes_to_native = [t] (bytes v) {
|
||||
return boost::any_cast<native_type>(t->deserialize(v));
|
||||
return value_cast<native_type>(t->deserialize(v));
|
||||
};
|
||||
auto c_to_bytes = [=] (c_type v) {
|
||||
return native_to_bytes(c_to_native(v));
|
||||
@@ -531,19 +521,19 @@ BOOST_AUTO_TEST_CASE(test_create_reverse_collection_type) {
|
||||
BOOST_REQUIRE(ri->is_collection());
|
||||
BOOST_REQUIRE(ri->is_multi_cell());
|
||||
|
||||
std::vector<boost::any> first_set;
|
||||
std::vector<data_value> first_set;
|
||||
bytes b1("1");
|
||||
bytes b2("2");
|
||||
first_set.push_back(boost::any(b1));
|
||||
first_set.push_back(boost::any(b2));
|
||||
first_set.push_back(b1);
|
||||
first_set.push_back(b2);
|
||||
|
||||
std::vector<boost::any> second_set;
|
||||
std::vector<data_value> second_set;
|
||||
bytes b3("2");
|
||||
second_set.push_back(boost::any(b1));
|
||||
second_set.push_back(boost::any(b3));
|
||||
second_set.push_back(b1);
|
||||
second_set.push_back(b3);
|
||||
|
||||
auto bv1 = my_set_type->decompose(first_set);
|
||||
auto bv2 = my_set_type->decompose(second_set);
|
||||
auto bv1 = my_set_type->decompose(make_set_value(my_set_type, first_set));
|
||||
auto bv2 = my_set_type->decompose(make_set_value(my_set_type, second_set));
|
||||
|
||||
auto straight_comp = my_set_type->compare(bytes_view(bv1), bytes_view(bv2));
|
||||
auto reverse_comp = ri->compare(bytes_view(bv2), bytes_view(bv2));
|
||||
|
||||
@@ -196,8 +196,8 @@ public:
|
||||
void write_string_list(std::vector<sstring> string_list);
|
||||
void write_bytes(bytes b);
|
||||
void write_short_bytes(bytes b);
|
||||
void write_option(std::pair<int16_t, boost::any> opt);
|
||||
void write_option_list(std::vector<std::pair<int16_t, boost::any>> opt_list);
|
||||
void write_option(std::pair<int16_t, data_value> opt);
|
||||
void write_option_list(std::vector<std::pair<int16_t, data_value>> opt_list);
|
||||
void write_inet(ipv4_addr inet);
|
||||
void write_consistency(db::consistency_level c);
|
||||
void write_string_map(std::map<sstring, sstring> string_map);
|
||||
@@ -1261,13 +1261,13 @@ void cql_server::response::write_short_bytes(bytes b)
|
||||
_body.insert(_body.end(), b.begin(), b.end());
|
||||
}
|
||||
|
||||
void cql_server::response::write_option(std::pair<int16_t, boost::any> opt)
|
||||
void cql_server::response::write_option(std::pair<int16_t, data_value> opt)
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void cql_server::response::write_option_list(std::vector<std::pair<int16_t, boost::any>> opt_list)
|
||||
void cql_server::response::write_option_list(std::vector<std::pair<int16_t, data_value>> opt_list)
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
|
||||
293
types.hh
293
types.hh
@@ -22,7 +22,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <experimental/optional>
|
||||
#include <boost/any.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -43,8 +42,10 @@
|
||||
#include <boost/range/numeric.hpp>
|
||||
#include <boost/range/combine.hpp>
|
||||
#include "net/ip.hh"
|
||||
#include <boost/multiprecision/cpp_int.hpp> // FIXME: remove somehow
|
||||
|
||||
class tuple_type_impl;
|
||||
class big_decimal;
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -188,25 +189,71 @@ inline int32_t compare_unsigned(bytes_view v1, bytes_view v2) {
|
||||
}
|
||||
|
||||
class abstract_type;
|
||||
class data_value;
|
||||
|
||||
using data_type = shared_ptr<const abstract_type>;
|
||||
|
||||
template <typename T>
|
||||
const T& value_cast(const data_value& value);
|
||||
|
||||
template <typename T>
|
||||
T&& value_cast(data_value&& value);
|
||||
|
||||
class data_value {
|
||||
boost::any _value;
|
||||
void* _value; // FIXME: use "small value optimization" for small types
|
||||
data_type _type;
|
||||
private:
|
||||
data_value(void* value, data_type type) : _value(value), _type(std::move(type)) {}
|
||||
template <typename T>
|
||||
static data_value make_new(data_type type, T&& value);
|
||||
public:
|
||||
data_value(boost::any&& value, data_type type)
|
||||
: _value{std::move(value)}
|
||||
, _type{type}
|
||||
{ }
|
||||
const boost::any& value() const {
|
||||
return _value;
|
||||
~data_value();
|
||||
data_value(const data_value&);
|
||||
data_value(data_value&& x) noexcept : _value(x._value), _type(std::move(x._type)) {
|
||||
x._value = nullptr;
|
||||
}
|
||||
data_type type() const {
|
||||
// common conversions from C++ types to database types
|
||||
// note: somewhat dangerous, consider a factory function instead
|
||||
data_value(bytes);
|
||||
data_value(sstring);
|
||||
data_value(int32_t);
|
||||
data_value(int64_t);
|
||||
data_value(utils::UUID);
|
||||
data_value(float);
|
||||
data_value(double);
|
||||
data_value(net::ipv4_address);
|
||||
data_value(db_clock::time_point);
|
||||
data_value(boost::multiprecision::cpp_int);
|
||||
data_value(big_decimal);
|
||||
template <typename NativeType>
|
||||
data_value(std::experimental::optional<NativeType>);
|
||||
|
||||
data_value& operator=(const data_value&);
|
||||
data_value& operator=(data_value&&);
|
||||
const data_type& type() const {
|
||||
return _type;
|
||||
}
|
||||
bool is_null() const { // may return false negatives for strings etc.
|
||||
return !_value;
|
||||
}
|
||||
friend inline bool operator==(const data_value& x, const data_value& y);
|
||||
friend inline bool operator!=(const data_value& x, const data_value& y);
|
||||
friend class abstract_type;
|
||||
static data_value make_null(data_type type) {
|
||||
return data_value(nullptr, std::move(type));
|
||||
}
|
||||
template <typename T>
|
||||
static data_value make(data_type type, std::unique_ptr<T> value) {
|
||||
return data_value(value.release(), std::move(type));
|
||||
}
|
||||
friend class empty_type_impl;
|
||||
template <typename T> friend const T& value_cast(const data_value&);
|
||||
template <typename T> friend T&& value_cast(data_value&&);
|
||||
friend data_value make_tuple_value(data_type, std::vector<data_value>);
|
||||
friend data_value make_set_value(data_type, std::vector<data_value>);
|
||||
friend data_value make_list_value(data_type, std::vector<data_value>);
|
||||
friend data_value make_map_value(data_type, std::vector<std::pair<data_value, data_value>>);
|
||||
friend data_value make_user_value(data_type, std::vector<data_value>);
|
||||
};
|
||||
|
||||
class serialized_compare;
|
||||
@@ -216,8 +263,14 @@ class abstract_type : public enable_shared_from_this<abstract_type> {
|
||||
public:
|
||||
abstract_type(sstring name) : _name(name) {}
|
||||
virtual ~abstract_type() {}
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const = 0;
|
||||
virtual size_t serialized_size(const boost::any& value) const = 0;
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const = 0;
|
||||
void serialize(const data_value& value, bytes::iterator& out) const {
|
||||
return serialize(get_value_ptr(value), out);
|
||||
}
|
||||
virtual size_t serialized_size(const void* value) const = 0;
|
||||
size_t serialized_size(const data_value& value) const {
|
||||
return serialized_size(get_value_ptr(value));
|
||||
}
|
||||
virtual bool less(bytes_view v1, bytes_view v2) const = 0;
|
||||
// returns a callable that can be called with two byte_views, and calls this->less() on them.
|
||||
serialized_compare as_less_comparator() const ;
|
||||
@@ -238,9 +291,9 @@ public:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
virtual boost::any deserialize(bytes_view v) const = 0;
|
||||
virtual data_value deserialize(bytes_view v) const = 0;
|
||||
data_value deserialize_value(bytes_view v) const {
|
||||
return data_value{deserialize(v), shared_from_this()};
|
||||
return deserialize(v);
|
||||
};
|
||||
virtual void validate(bytes_view v) const {
|
||||
// FIXME
|
||||
@@ -287,10 +340,13 @@ protected:
|
||||
return is_compatible_with(other);
|
||||
}
|
||||
public:
|
||||
bytes decompose(const boost::any& value) const {
|
||||
bytes b(bytes::initialized_later(), serialized_size(value));
|
||||
bytes decompose(const data_value& value) const {
|
||||
if (!value._value) {
|
||||
return {};
|
||||
}
|
||||
bytes b(bytes::initialized_later(), serialized_size(value._value));
|
||||
auto i = b.begin();
|
||||
serialize(value, i);
|
||||
serialize(value._value, i);
|
||||
return b;
|
||||
}
|
||||
sstring name() const {
|
||||
@@ -324,11 +380,112 @@ public:
|
||||
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const = 0;
|
||||
virtual shared_ptr<const abstract_type> freeze() const { return shared_from_this(); }
|
||||
friend class list_type_impl;
|
||||
protected:
|
||||
// native_value_* methods are virualized versions of native_type's
|
||||
// sizeof/alignof/copy-ctor/move-ctor etc.
|
||||
virtual size_t native_value_size() const = 0;
|
||||
virtual size_t native_value_alignment() const = 0;
|
||||
virtual void native_value_copy(const void* from, void* to) const = 0;
|
||||
virtual void native_value_move(const void* from, void* to) const = 0;
|
||||
virtual void* native_value_clone(const void* from) const = 0;
|
||||
virtual void native_value_destroy(void* object) const = 0;
|
||||
virtual void native_value_delete(void* object) const = 0;
|
||||
virtual const std::type_info& native_typeid() const = 0;
|
||||
// abstract_type is a friend of data_value, but derived classes are not.
|
||||
static const void* get_value_ptr(const data_value& v) {
|
||||
return v._value;
|
||||
}
|
||||
friend void write_collection_value(bytes::iterator& out, serialization_format sf, data_type type, const data_value& value);
|
||||
friend class tuple_type_impl;
|
||||
friend class data_value;
|
||||
friend class reversed_type_impl;
|
||||
template <typename T> friend const T& value_cast(const data_value& value);
|
||||
template <typename T> friend T&& value_cast(data_value&& value);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
data_value
|
||||
data_value::make_new(data_type type, T&& value) {
|
||||
return data_value(type->native_value_clone(&value), type);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& value_cast(const data_value& value) {
|
||||
if (typeid(T) != value.type()->native_typeid()) {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
if (value.is_null()) {
|
||||
throw std::runtime_error("value is null");
|
||||
}
|
||||
return *reinterpret_cast<T*>(value._value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T&& value_cast(data_value&& value) {
|
||||
if (typeid(T) != value.type()->native_typeid()) {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
if (value.is_null()) {
|
||||
throw std::runtime_error("value is null");
|
||||
}
|
||||
return std::move(*reinterpret_cast<T*>(value._value));
|
||||
}
|
||||
|
||||
// CRTP: implements translation between a native_type (C++ type) to abstract_type
|
||||
// AbstractType is parametrized because we want a
|
||||
// abstract_type -> collection_type_impl -> map_type
|
||||
// type hierarchy, and native_type is only known at the last step.
|
||||
template <typename NativeType, typename AbstractType = abstract_type>
|
||||
class concrete_type : public AbstractType {
|
||||
public:
|
||||
using native_type = NativeType;
|
||||
using AbstractType::AbstractType;
|
||||
protected:
|
||||
virtual size_t native_value_size() const override {
|
||||
return sizeof(native_type);
|
||||
}
|
||||
virtual size_t native_value_alignment() const override {
|
||||
return alignof(native_type);
|
||||
}
|
||||
virtual void native_value_copy(const void* from, void* to) const override {
|
||||
new (to) native_type(*reinterpret_cast<const native_type*>(from));
|
||||
}
|
||||
virtual void native_value_move(const void* from, void* to) const override {
|
||||
new (to) native_type(std::move(*reinterpret_cast<const native_type*>(from)));
|
||||
}
|
||||
virtual void native_value_destroy(void* object) const override {
|
||||
reinterpret_cast<native_type*>(object)->~native_type();
|
||||
}
|
||||
virtual void native_value_delete(void* object) const override {
|
||||
delete reinterpret_cast<native_type*>(object);
|
||||
}
|
||||
virtual void* native_value_clone(const void* object) const override {
|
||||
return new native_type(*reinterpret_cast<const native_type*>(object));
|
||||
}
|
||||
virtual const std::type_info& native_typeid() const override {
|
||||
return typeid(native_type);
|
||||
}
|
||||
protected:
|
||||
data_value make_value(std::unique_ptr<NativeType> value) const {
|
||||
return data_value::make(this->shared_from_this(), std::move(value));
|
||||
}
|
||||
data_value make_value(NativeType value) const {
|
||||
return make_value(std::make_unique<NativeType>(std::move(value)));
|
||||
}
|
||||
data_value make_null() const {
|
||||
return data_value::make_null(this->shared_from_this());
|
||||
}
|
||||
const NativeType& from_value(const void* v) const {
|
||||
return *reinterpret_cast<const NativeType*>(v);
|
||||
}
|
||||
const NativeType& from_value(const data_value& v) const {
|
||||
return this->from_value(AbstractType::get_value_ptr(v));
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const data_value& x, const data_value& y)
|
||||
{
|
||||
return x._type->equals(y._type) && x._type->equal(x._type->decompose(x._value), y._type->decompose(y._value));
|
||||
return x._type->equals(y._type) && x._type->equal(x._type->decompose(x), y._type->decompose(y));
|
||||
}
|
||||
|
||||
inline bool operator!=(const data_value& x, const data_value& y)
|
||||
@@ -448,10 +605,10 @@ public:
|
||||
collection_mutation::one serialize_mutation_form_only_live(mutation_view mut, gc_clock::time_point now) const;
|
||||
collection_mutation::one merge(collection_mutation::view a, collection_mutation::view b) const;
|
||||
collection_mutation::one difference(collection_mutation::view a, collection_mutation::view b) const;
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const = 0;
|
||||
virtual boost::any deserialize(bytes_view v, serialization_format sf) const = 0;
|
||||
virtual void serialize(const void* value, bytes::iterator& out, serialization_format sf) const = 0;
|
||||
virtual data_value deserialize(bytes_view v, serialization_format sf) const = 0;
|
||||
data_value deserialize_value(bytes_view v, serialization_format sf) const {
|
||||
return data_value{deserialize(v, sf), shared_from_this()};
|
||||
return deserialize(v, sf);
|
||||
}
|
||||
bytes_opt reserialize(serialization_format from, serialization_format to, bytes_view_opt v) const;
|
||||
};
|
||||
@@ -563,13 +720,13 @@ public:
|
||||
return _underlying_type->is_multi_cell();
|
||||
}
|
||||
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const override {
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const override {
|
||||
_underlying_type->serialize(value, out);
|
||||
}
|
||||
virtual size_t serialized_size(const boost::any& value) const {
|
||||
virtual size_t serialized_size(const void* value) const override {
|
||||
return _underlying_type->serialized_size(value);
|
||||
}
|
||||
virtual boost::any deserialize(bytes_view v) const override {
|
||||
virtual data_value deserialize(bytes_view v) const override {
|
||||
return _underlying_type->deserialize(v);
|
||||
}
|
||||
|
||||
@@ -590,10 +747,22 @@ public:
|
||||
static shared_ptr<const reversed_type_impl> get_instance(data_type type) {
|
||||
return intern::get_instance(std::move(type));
|
||||
}
|
||||
protected:
|
||||
virtual size_t native_value_size() const override;
|
||||
virtual size_t native_value_alignment() const override;
|
||||
virtual void native_value_copy(const void* from, void* to) const override;
|
||||
virtual void native_value_move(const void* from, void* to) const override;
|
||||
virtual void native_value_destroy(void* object) const override;
|
||||
virtual void* native_value_clone(const void* object) const override;
|
||||
virtual void native_value_delete(void* object) const override;
|
||||
virtual const std::type_info& native_typeid() const override;
|
||||
};
|
||||
using reversed_type = shared_ptr<const reversed_type_impl>;
|
||||
|
||||
class map_type_impl final : public collection_type_impl {
|
||||
template <typename NativeType>
|
||||
using concrete_collection_type = concrete_type<NativeType, collection_type_impl>;
|
||||
|
||||
class map_type_impl final : public concrete_collection_type<std::vector<std::pair<data_value, data_value>>> {
|
||||
using map_type = shared_ptr<const map_type_impl>;
|
||||
using intern = type_interning_helper<map_type_impl, data_type, data_type, bool>;
|
||||
data_type _keys;
|
||||
@@ -603,9 +772,6 @@ class map_type_impl final : public collection_type_impl {
|
||||
protected:
|
||||
virtual sstring cql3_type_name() const override;
|
||||
public:
|
||||
// type returned by deserialize() and expected by serialize
|
||||
// does not support mutations/ttl/tombstone - purely for I/O.
|
||||
using native_type = std::vector<std::pair<boost::any, boost::any>>;
|
||||
static shared_ptr<const map_type_impl> get_instance(data_type keys, data_type values, bool is_multi_cell);
|
||||
map_type_impl(data_type keys, data_type values, bool is_multi_cell);
|
||||
data_type get_keys_type() const { return _keys; }
|
||||
@@ -620,11 +786,11 @@ public:
|
||||
static int32_t compare_maps(data_type keys_comparator, data_type values_comparator,
|
||||
bytes_view o1, bytes_view o2);
|
||||
virtual bool is_byte_order_comparable() const override { return false; }
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const boost::any& value) const;
|
||||
virtual boost::any deserialize(bytes_view v) const override;
|
||||
virtual boost::any deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const void* value) const;
|
||||
virtual data_value deserialize(bytes_view v) const override;
|
||||
virtual data_value deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual sstring to_string(const bytes& b) const override;
|
||||
virtual size_t hash(bytes_view v) const override;
|
||||
virtual bytes from_string(sstring_view text) const override;
|
||||
@@ -636,7 +802,9 @@ public:
|
||||
|
||||
using map_type = shared_ptr<const map_type_impl>;
|
||||
|
||||
class set_type_impl final : public collection_type_impl {
|
||||
data_value make_map_value(data_type tuple_type, map_type_impl::native_type value);
|
||||
|
||||
class set_type_impl final : public concrete_collection_type<std::vector<data_value>> {
|
||||
using set_type = shared_ptr<const set_type_impl>;
|
||||
using intern = type_interning_helper<set_type_impl, data_type, bool>;
|
||||
data_type _elements;
|
||||
@@ -644,9 +812,6 @@ class set_type_impl final : public collection_type_impl {
|
||||
protected:
|
||||
virtual sstring cql3_type_name() const override;
|
||||
public:
|
||||
// type returned by deserialize() and expected by serialize
|
||||
// does not support mutations/ttl/tombstone - purely for I/O.
|
||||
using native_type = std::vector<boost::any>;
|
||||
static set_type get_instance(data_type elements, bool is_multi_cell);
|
||||
set_type_impl(data_type elements, bool is_multi_cell);
|
||||
data_type get_elements_type() const { return _elements; }
|
||||
@@ -658,11 +823,11 @@ public:
|
||||
virtual bool is_value_compatible_with_frozen(const collection_type_impl& previous) const override;
|
||||
virtual bool less(bytes_view o1, bytes_view o2) const override;
|
||||
virtual bool is_byte_order_comparable() const override { return _elements->is_byte_order_comparable(); }
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const boost::any& value) const override;
|
||||
virtual boost::any deserialize(bytes_view v) const override;
|
||||
virtual boost::any deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const void* value) const override;
|
||||
virtual data_value deserialize(bytes_view v) const override;
|
||||
virtual data_value deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual sstring to_string(const bytes& b) const override;
|
||||
virtual size_t hash(bytes_view v) const override;
|
||||
virtual bytes from_string(sstring_view text) const override;
|
||||
@@ -670,12 +835,13 @@ public:
|
||||
virtual bytes to_value(mutation_view mut, serialization_format sf) const override;
|
||||
bytes serialize_partially_deserialized_form(
|
||||
const std::vector<bytes_view>& v, serialization_format sf) const;
|
||||
|
||||
};
|
||||
|
||||
using set_type = shared_ptr<const set_type_impl>;
|
||||
|
||||
class list_type_impl final : public collection_type_impl {
|
||||
data_value make_set_value(data_type tuple_type, set_type_impl::native_type value);
|
||||
|
||||
class list_type_impl final : public concrete_collection_type<std::vector<data_value>> {
|
||||
using list_type = shared_ptr<const list_type_impl>;
|
||||
using intern = type_interning_helper<list_type_impl, data_type, bool>;
|
||||
data_type _elements;
|
||||
@@ -683,9 +849,6 @@ class list_type_impl final : public collection_type_impl {
|
||||
protected:
|
||||
virtual sstring cql3_type_name() const override;
|
||||
public:
|
||||
// type returned by deserialize() and expected by serialize
|
||||
// does not support mutations/ttl/tombstone - purely for I/O.
|
||||
using native_type = std::vector<boost::any>;
|
||||
static list_type get_instance(data_type elements, bool is_multi_cell);
|
||||
list_type_impl(data_type elements, bool is_multi_cell);
|
||||
data_type get_elements_type() const { return _elements; }
|
||||
@@ -697,11 +860,11 @@ public:
|
||||
virtual bool is_value_compatible_with_frozen(const collection_type_impl& previous) const override;
|
||||
virtual bool less(bytes_view o1, bytes_view o2) const override;
|
||||
// FIXME: origin doesn't override is_byte_order_comparable(). Why?
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const boost::any& value) const override;
|
||||
virtual boost::any deserialize(bytes_view v) const override;
|
||||
virtual boost::any deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out, serialization_format sf) const override;
|
||||
virtual size_t serialized_size(const void* value) const override;
|
||||
virtual data_value deserialize(bytes_view v) const override;
|
||||
virtual data_value deserialize(bytes_view v, serialization_format sf) const override;
|
||||
virtual sstring to_string(const bytes& b) const override;
|
||||
virtual size_t hash(bytes_view v) const override;
|
||||
virtual bytes from_string(sstring_view text) const override;
|
||||
@@ -711,6 +874,8 @@ public:
|
||||
|
||||
using list_type = shared_ptr<const list_type_impl>;
|
||||
|
||||
data_value make_list_value(data_type type, list_type_impl::native_type value);
|
||||
|
||||
inline
|
||||
size_t hash_value(const shared_ptr<const abstract_type>& x) {
|
||||
return std::hash<const abstract_type*>()(x.get());
|
||||
@@ -945,7 +1110,7 @@ read_simple_bytes(bytes_view& v, size_t n) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
boost::any read_simple_opt(bytes_view& v) {
|
||||
std::experimental::optional<T> read_simple_opt(bytes_view& v) {
|
||||
if (v.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -954,7 +1119,7 @@ boost::any read_simple_opt(bytes_view& v) {
|
||||
}
|
||||
auto p = v.begin();
|
||||
v.remove_prefix(sizeof(T));
|
||||
return boost::any(net::ntoh(*reinterpret_cast<const net::packed<T>*>(p)));
|
||||
return { net::ntoh(*reinterpret_cast<const net::packed<T>*>(p)) };
|
||||
}
|
||||
|
||||
inline sstring read_simple_short_string(bytes_view& v) {
|
||||
@@ -972,7 +1137,7 @@ size_t collection_size_len(serialization_format sf);
|
||||
size_t collection_value_len(serialization_format sf);
|
||||
void write_collection_size(bytes::iterator& out, int size, serialization_format sf);
|
||||
void write_collection_value(bytes::iterator& out, serialization_format sf, bytes_view val_bytes);
|
||||
void write_collection_value(bytes::iterator& out, serialization_format sf, data_type type, const boost::any& value);
|
||||
void write_collection_value(bytes::iterator& out, serialization_format sf, data_type type, const data_value& value);
|
||||
|
||||
template <typename BytesViewIterator>
|
||||
bytes
|
||||
@@ -1050,7 +1215,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class tuple_type_impl : public abstract_type {
|
||||
class tuple_type_impl : public concrete_type<std::vector<data_value>> {
|
||||
protected:
|
||||
std::vector<data_type> _types;
|
||||
static boost::iterator_range<tuple_deserializing_iterator> make_range(bytes_view v) {
|
||||
@@ -1058,7 +1223,6 @@ protected:
|
||||
}
|
||||
tuple_type_impl(sstring name, std::vector<data_type> types);
|
||||
public:
|
||||
using native_type = std::vector<boost::any>;
|
||||
tuple_type_impl(std::vector<data_type> types);
|
||||
static shared_ptr<tuple_type_impl> get_instance(std::vector<data_type> types);
|
||||
data_type type(size_t i) const {
|
||||
@@ -1072,9 +1236,9 @@ public:
|
||||
}
|
||||
virtual int32_t compare(bytes_view v1, bytes_view v2) const override;
|
||||
virtual bool less(bytes_view v1, bytes_view v2) const override;
|
||||
virtual size_t serialized_size(const boost::any& value) const override;
|
||||
virtual void serialize(const boost::any& value, bytes::iterator& out) const override;
|
||||
virtual boost::any deserialize(bytes_view v) const override;
|
||||
virtual size_t serialized_size(const void* value) const override;
|
||||
virtual void serialize(const void* value, bytes::iterator& out) const override;
|
||||
virtual data_value deserialize(bytes_view v) const override;
|
||||
std::vector<bytes_view_opt> split(bytes_view v) const;
|
||||
template <typename RangeOf_bytes_opt> // also accepts bytes_view_opt
|
||||
static bytes build_value(RangeOf_bytes_opt&& range) {
|
||||
@@ -1106,6 +1270,8 @@ private:
|
||||
static sstring make_name(const std::vector<data_type>& types);
|
||||
};
|
||||
|
||||
data_value make_tuple_value(data_type tuple_type, tuple_type_impl::native_type value);
|
||||
|
||||
class user_type_impl : public tuple_type_impl {
|
||||
public:
|
||||
const sstring _keyspace;
|
||||
@@ -1113,6 +1279,7 @@ public:
|
||||
private:
|
||||
std::vector<bytes> _field_names;
|
||||
public:
|
||||
using native_type = std::vector<data_value>;
|
||||
user_type_impl(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types)
|
||||
: tuple_type_impl(make_name(keyspace, name, field_names, field_types), field_types)
|
||||
, _keyspace(keyspace)
|
||||
@@ -1132,5 +1299,13 @@ private:
|
||||
static sstring make_name(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types);
|
||||
};
|
||||
|
||||
data_value make_user_value(data_type tuple_type, user_type_impl::native_type value);
|
||||
|
||||
using user_type = shared_ptr<const user_type_impl>;
|
||||
using tuple_type = shared_ptr<const tuple_type_impl>;
|
||||
|
||||
template <typename NativeType>
|
||||
data_value::data_value(std::experimental::optional<NativeType> v)
|
||||
: data_value(v ? data_value(*v) : data_value::make_null(data_type_for<NativeType>())) {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user