/* * Copyright (C) 2015 Cloudius Systems, Ltd. */ #include #include #include "cql3/cql3_type.hh" #include "types.hh" #include "core/print.hh" #include "net/ip.hh" #include "database.hh" #include "util/serialization.hh" #include "combine.hh" #include #include #include #include #include #include #include #include #include template struct simple_type_traits { static T read_nonempty(bytes_view v) { return read_simple_exactly(v); } }; template<> struct simple_type_traits { static bool read_nonempty(bytes_view v) { return read_simple_exactly(v) != 0; } }; template<> struct simple_type_traits { static db_clock::time_point read_nonempty(bytes_view v) { return db_clock::time_point(db_clock::duration(read_simple_exactly(v))); } }; template struct simple_type_impl : abstract_type { simple_type_impl(sstring name) : abstract_type(std::move(name)) {} virtual int32_t compare(bytes_view v1, bytes_view v2) const override { if (v1.empty()) { return v2.empty() ? 0 : -1; } if (v2.empty()) { return 1; } T a = simple_type_traits::read_nonempty(v1); T b = simple_type_traits::read_nonempty(v2); return a == b ? 0 : a < b ? -1 : 1; } virtual bool less(bytes_view v1, bytes_view v2) const override { return compare(v1, v2) < 0; } virtual bool is_byte_order_equal() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } }; template struct integer_type_impl : simple_type_impl { integer_type_impl(sstring name) : simple_type_impl(name) {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto v = boost::any_cast(value); auto u = net::hton(v); out = std::copy_n(reinterpret_cast(&u), sizeof(u), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } auto v = boost::any_cast(value); return sizeof(v); } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } return read_simple_opt(v); } T compose_value(const bytes& b) const { if (b.size() != sizeof(T)) { throw marshal_exception(); } return (T)net::ntoh(*reinterpret_cast(b.begin())); } bytes decompose_value(T v) const { bytes b(bytes::initialized_later(), sizeof(v)); *reinterpret_cast(b.begin()) = (T)net::hton(v); return b; } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != sizeof(T)) { throw marshal_exception(); } } T parse_int(sstring_view s) const { try { return boost::lexical_cast(s.begin(), s.size()); } catch (const boost::bad_lexical_cast& e) { throw marshal_exception(sprint("Invalid number format '%s'", s)); } } virtual bytes from_string(sstring_view s) const override { return decompose_value(parse_int(s)); } virtual sstring to_string(const bytes& b) const override { if (b.empty()) { return {}; } return to_sstring(compose_value(b)); } }; struct int32_type_impl : integer_type_impl { int32_type_impl() : integer_type_impl{"org.apache.cassandra.db.marshal.Int32Type"} { } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::int_; } }; struct long_type_impl : integer_type_impl { long_type_impl() : integer_type_impl{"org.apache.cassandra.db.marshal.LongType"} { } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::bigint; } virtual bool is_value_compatible_with_internal(const abstract_type& other) const override { return &other == this || &other == date_type.get() || &other == timestamp_type.get(); } }; struct string_type_impl : public abstract_type { string_type_impl(sstring name, std::function()> cql3_type) : abstract_type(name), _cql3_type(cql3_type) {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto& v = boost::any_cast(value); out = std::copy(v.begin(), v.end(), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } auto& v = boost::any_cast(value); return v.size(); } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } // FIXME: validation? return boost::any(sstring(reinterpret_cast(v.begin()), v.size())); } virtual bool less(bytes_view v1, bytes_view v2) const override { return less_unsigned(v1, v2); } virtual bool is_byte_order_equal() const override { return true; } virtual bool is_byte_order_comparable() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual void validate(bytes_view v) const override { if (as_cql3_type() == cql3::cql3_type::ascii) { if (std::any_of(v.begin(), v.end(), [] (int8_t b) { return b < 0; })) { throw marshal_exception(); } } else { try { boost::locale::conv::utf_to_utf(v.data(), boost::locale::conv::stop); } catch (const boost::locale::conv::conversion_error& ex) { throw marshal_exception(ex.what()); } } } virtual bytes from_string(sstring_view s) const override { return to_bytes(bytes_view(reinterpret_cast(s.begin()), s.size())); } virtual sstring to_string(const bytes& b) const override { return sstring(reinterpret_cast(b.begin()), b.size()); } virtual ::shared_ptr as_cql3_type() const override { return _cql3_type(); } std::function()> _cql3_type; }; struct bytes_type_impl final : public abstract_type { bytes_type_impl() : abstract_type("org.apache.cassandra.db.marshal.BytesType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto& v = boost::any_cast(value); out = std::copy(v.begin(), v.end(), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } auto& v = boost::any_cast(value); return v.size(); } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } return boost::any(bytes(v.begin(), v.end())); } virtual bool less(bytes_view v1, bytes_view v2) const override { return less_unsigned(v1, v2); } virtual bool is_byte_order_equal() const override { return true; } virtual bool is_byte_order_comparable() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual bytes from_string(sstring_view s) const override { return from_hex(s); } virtual sstring to_string(const bytes& b) const override { return to_hex(b); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::blob; } virtual bool is_value_compatible_with_internal(const abstract_type& other) const override { return true; } }; struct boolean_type_impl : public simple_type_impl { boolean_type_impl() : simple_type_impl("org.apache.cassandra.db.marshal.BooleanType") {} void serialize_value(bool value, bytes::iterator& out) const { *out++ = char(value); } virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } serialize_value(boost::any_cast(value), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 1; } size_t serialized_size(bool value) const { return 1; } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } if (v.size() != 1) { throw marshal_exception(); } return boost::any(*v.begin() != 0); } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != 1) { throw marshal_exception(); } } virtual bytes from_string(sstring_view s) const override { sstring s_lower(s.begin(), s.end()); std::transform(s_lower.begin(), s_lower.end(), s_lower.begin(), ::tolower); if (s.empty() || s_lower == "false") { return ::serialize_value(*this, false); } else if (s_lower == "true") { return ::serialize_value(*this, true); } else { throw marshal_exception(sprint("unable to make boolean from '%s'", s)); } } virtual sstring to_string(const bytes& b) const override { if (b.empty()) { return ""; } if (b.size() != 1) { throw marshal_exception(); } return *b.begin() ? "true" : "false"; } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::boolean; } }; struct date_type_impl : public abstract_type { date_type_impl() : abstract_type("date") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto v = boost::any_cast(value); int64_t i = v.time_since_epoch().count(); i = net::hton(uint64_t(i)); out = std::copy_n(reinterpret_cast(&i), sizeof(i), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 8; } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } auto tmp = read_simple_exactly(v); return boost::any(db_clock::time_point(db_clock::duration(tmp))); } virtual bool less(bytes_view b1, bytes_view b2) const override { return compare_unsigned(b1, b2); } virtual bool is_byte_order_comparable() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual bytes from_string(sstring_view s) const override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) const override { throw std::runtime_error("not implemented"); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::timestamp; } virtual bool is_value_compatible_with_internal(const abstract_type& other) const override { return &other == this || &other == timestamp_type.get() || &other == long_type.get(); } }; struct timeuuid_type_impl : public abstract_type { timeuuid_type_impl() : abstract_type("org.apache.cassandra.db.marshal.TimeUUIDType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto& uuid = boost::any_cast(value); out = std::copy_n(uuid.to_bytes().begin(), sizeof(uuid), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 16; } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } uint64_t msb, lsb; if (v.empty()) { return {}; } msb = read_simple(v); lsb = read_simple(v); if (!v.empty()) { throw marshal_exception(); } return boost::any(utils::UUID(msb, lsb)); } virtual bool less(bytes_view b1, bytes_view b2) const override { if (b1.empty()) { return b2.empty() ? false : true; } if (b2.empty()) { return false; } auto r = compare_bytes(b1, b2); if (r != 0) { return r < 0; } else { return std::lexicographical_compare(b1.begin(), b1.end(), b2.begin(), b2.end()); } } virtual bool is_byte_order_equal() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != 16) { throw marshal_exception(); } auto msb = read_simple(v); auto lsb = read_simple(v); utils::UUID uuid(msb, lsb); if (uuid.version() != 1) { throw marshal_exception(); } } virtual bytes from_string(sstring_view s) const override { if (s.empty()) { return bytes(); } static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"); if (!std::regex_match(s.data(), re)) { throw marshal_exception(); } utils::UUID v(s); if (v.version() != 1) { throw marshal_exception(); } return v.to_bytes(); } virtual sstring to_string(const bytes& b) const override { auto v = deserialize(b); if (v.empty()) { return ""; } return boost::any_cast(v).to_sstring(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::timeuuid; } private: static int compare_bytes(bytes_view o1, bytes_view o2) { auto compare_pos = [&] (unsigned pos, int mask, int ifequal) { int d = (o1[pos] & mask) - (o2[pos] & mask); return d ? d : ifequal; }; return compare_pos(6, 0xf, compare_pos(7, 0xff, compare_pos(4, 0xff, compare_pos(5, 0xff, compare_pos(0, 0xff, compare_pos(1, 0xff, compare_pos(2, 0xff, compare_pos(3, 0xff, 0)))))))); } friend class uuid_type_impl; }; struct timestamp_type_impl : simple_type_impl { timestamp_type_impl() : simple_type_impl("org.apache.cassandra.db.marshal.TimestampType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } uint64_t v = boost::any_cast(value).time_since_epoch().count(); v = net::hton(v); out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 8; } virtual boost::any deserialize(bytes_view in) const override { if (in.empty()) { return {}; } auto v = read_simple_exactly(in); return boost::any(db_clock::time_point(db_clock::duration(v))); } // FIXME: isCompatibleWith(timestampuuid) virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != sizeof(uint64_t)) { throw marshal_exception(); } } boost::posix_time::ptime get_time(const std::string& s) const { // Apparently, the code below doesn't leak the input facet. // std::locale::facet has some internal, custom reference counting // and deletes the object when it's no longer used. auto tif = new boost::posix_time::time_input_facet("%Y-%m-%d %H:%M:%S%F"); std::istringstream ss(s); ss.imbue(std::locale(ss.getloc(), tif)); boost::posix_time::ptime t; ss >> t; if (ss.fail() || ss.peek() != std::istringstream::traits_type::eof()) { throw marshal_exception(); } return t; } boost::posix_time::time_duration get_utc_offset(const std::string& s) const { static constexpr const char* formats[] = { "%H:%M", "%H%M", }; for (auto&& f : formats) { auto tif = new boost::posix_time::time_input_facet(f); std::istringstream ss(s); ss.imbue(std::locale(ss.getloc(), tif)); auto sign = ss.get(); boost::posix_time::ptime p; ss >> p; if (ss.good() && ss.peek() == std::istringstream::traits_type::eof()) { return p.time_of_day() * (sign == '-' ? -1 : 1); } } throw marshal_exception(); } int64_t timestamp_from_string(sstring_view s) const { std::string str; str.resize(s.size()); std::transform(s.begin(), s.end(), str.begin(), ::tolower); if (str == "now") { return db_clock::now().time_since_epoch().count(); } char* end; auto v = std::strtoll(s.begin(), &end, 10); if (end == s.begin() + s.size()) { return v; } std::regex date_re("^\\d{4}-\\d{2}-\\d{2}([ t]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?)?"); std::smatch dsm; if (!std::regex_search(str, dsm, date_re)) { throw marshal_exception(); } auto t = get_time(dsm.str()); auto tz = dsm.suffix().str(); std::regex tz_re("([\\+-]\\d{2}:?(\\d{2})?)"); std::smatch tsm; if (std::regex_match(tz, tsm, tz_re)) { t -= get_utc_offset(tsm.str()); } else if (tz.empty()) { typedef boost::date_time::c_local_adjustor local_tz; // local_tz::local_to_utc(), where are you? auto t1 = local_tz::utc_to_local(t); auto tz_offset = t1 - t; auto t2 = local_tz::utc_to_local(t - tz_offset); auto dst_offset = t2 - t; t -= tz_offset + dst_offset; } else { throw marshal_exception(); } return (t - boost::posix_time::from_time_t(0)).total_milliseconds(); } virtual bytes from_string(sstring_view s) const override { if (s.empty()) { return bytes(); } int64_t ts; try { ts = timestamp_from_string(s); } catch (...) { throw marshal_exception(sprint("unable to parse date '%s'", s)); } bytes b(bytes::initialized_later(), sizeof(int64_t)); *unaligned_cast(b.begin()) = net::hton(ts); return b; } virtual sstring to_string(const bytes& b) const override { throw std::runtime_error("not implemented"); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::timestamp; } virtual bool is_value_compatible_with_internal(const abstract_type& other) const override { return &other == this || &other == date_type.get() || &other == long_type.get(); } }; struct uuid_type_impl : abstract_type { uuid_type_impl() : abstract_type("org.apache.cassandra.db.marshal.UUIDType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } auto& uuid = boost::any_cast(value); out = std::copy_n(uuid.to_bytes().begin(), sizeof(uuid), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 16; } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } auto msb = read_simple(v); auto lsb = read_simple(v); if (!v.empty()) { throw marshal_exception(); } return boost::any(utils::UUID(msb, lsb)); } virtual bool less(bytes_view b1, bytes_view b2) const override { if (b1.size() < 16) { return b2.size() < 16 ? false : true; } if (b2.size() < 16) { return false; } auto v1 = (b1[6] >> 4) & 0x0f; auto v2 = (b2[6] >> 4) & 0x0f; if (v1 != v2) { return v1 < v2; } if (v1 == 1) { auto c1 = timeuuid_type_impl::compare_bytes(b1, b2); auto c2 = timeuuid_type_impl::compare_bytes(b2, b1); // Require strict ordering if (c1 != c2) { return c1; } } return less_unsigned(b1, b2); } // FIXME: isCompatibleWith(uuid) virtual bool is_byte_order_equal() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != 16) { throw marshal_exception(); } } virtual bytes from_string(sstring_view s) const override { if (s.empty()) { return bytes(); } static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"); if (!std::regex_match(s.data(), re)) { throw marshal_exception(); } utils::UUID v(s); return v.to_bytes(); } virtual sstring to_string(const bytes& b) const override { auto v = deserialize(b); if (v.empty()) { return ""; } return boost::any_cast(v).to_sstring(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::uuid; } virtual bool is_value_compatible_with_internal(const abstract_type& other) const override { return &other == this || &other == timeuuid_type.get(); } }; struct inet_addr_type_impl : abstract_type { inet_addr_type_impl() : abstract_type("org.apache.cassandra.db.marshal.InetAddressType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } // FIXME: support ipv6 auto& ipv4 = boost::any_cast(value); uint32_t u = htonl(ipv4.ip); out = std::copy_n(reinterpret_cast(&u), sizeof(u), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return 4; } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } if (v.size() == 16) { throw std::runtime_error("IPv6 addresses not supported"); } auto ip = read_simple(v); if (!v.empty()) { throw marshal_exception(); } return boost::any(net::ipv4_address(ip)); } virtual bool less(bytes_view v1, bytes_view v2) const override { return less_unsigned(v1, v2); } virtual bool is_byte_order_equal() const override { return true; } virtual bool is_byte_order_comparable() const override { return true; } virtual size_t hash(bytes_view v) const override { return std::hash()(v); } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != sizeof(uint32_t)) { throw marshal_exception(); } } virtual bytes from_string(sstring_view s) const override { // FIXME: support host names if (s.empty()) { return bytes(); } net::ipv4_address ipv4; try { ipv4 = net::ipv4_address(s.data()); } catch (...) { throw marshal_exception(); } bytes b(bytes::initialized_later(), sizeof(uint32_t)); auto out = b.begin(); serialize(boost::any(ipv4), out); return b; } virtual sstring to_string(const bytes& b) const override { auto v = deserialize(b); if (v.empty()) { return ""; } boost::asio::ip::address_v4 ipv4(boost::any_cast(v).ip); return ipv4.to_string(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::inet; } }; // Integer of same length of a given type. This is useful because our // ntoh functions only know how to operate on integers. template struct int_of_size; template struct int_of_size_impl { using dtype = D; using itype = I; static_assert(sizeof(dtype) == sizeof(itype), "size mismatch"); static_assert(alignof(dtype) == alignof(itype), "align mismatch"); }; template <> struct int_of_size : public int_of_size_impl {}; template <> struct int_of_size : public int_of_size_impl {}; template struct float_type_traits { static double read_nonempty(bytes_view v) { union { T d; typename int_of_size::itype i; } x; x.i = read_simple_exactly::itype>(v); return x.d; } }; template<> struct simple_type_traits : public float_type_traits {}; template<> struct simple_type_traits : public float_type_traits {}; template struct floating_type_impl : public simple_type_impl { floating_type_impl(sstring name) : simple_type_impl(std::move(name)) {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { if (value.empty()) { return; } T d = boost::any_cast(value); if (std::isnan(d)) { // Java's Double.doubleToLongBits() documentation specifies that // any nan must be serialized to the same specific value d = std::numeric_limits::quiet_NaN(); } union { T d; typename int_of_size::itype i; } x; x.d = d; auto u = net::hton(x.i); out = std::copy_n(reinterpret_cast(&u), sizeof(u), out); } virtual size_t serialized_size(const boost::any& value) const override { if (value.empty()) { return 0; } return sizeof(T); } virtual boost::any deserialize(bytes_view v) const override { if (v.empty()) { return {}; } union { T d; typename int_of_size::itype i; } x; x.i = read_simple::itype>(v); if (!v.empty()) { throw marshal_exception(); } return boost::any(x.d); } virtual int32_t compare(bytes_view v1, bytes_view v2) const override { if (v1.empty()) { return v2.empty() ? 0 : -1; } if (v2.empty()) { return 1; } T a = simple_type_traits::read_nonempty(v1); T b = simple_type_traits::read_nonempty(v2); // in java world NaN == NaN and NaN is greater than anything else if (std::isnan(a) && std::isnan(b)) { return 0; } else if (std::isnan(a)) { return 1; } else if (std::isnan(b)) { return -1; } // also -0 < 0 if (std::signbit(a) && !std::signbit(b)) { return -1; } else if (!std::signbit(a) && std::signbit(b)) { return 1; } return a == b ? 0 : a < b ? -1 : 1; } virtual void validate(bytes_view v) const override { if (v.size() != 0 && v.size() != sizeof(T)) { throw marshal_exception(); } } virtual bytes from_string(sstring_view s) const override { if (s.empty()) { return bytes(); } try { auto d = boost::lexical_cast(s.begin(), s.size()); bytes b(bytes::initialized_later(), sizeof(T)); auto out = b.begin(); serialize(boost::any(d), out); return b; } catch(const boost::bad_lexical_cast& e) { throw marshal_exception(sprint("Invalid number format '%s'", s)); } } virtual sstring to_string(const bytes& b) const override { auto v = deserialize(b); if (v.empty()) { return ""; } return to_sstring(boost::any_cast(v)); } }; struct double_type_impl : floating_type_impl { double_type_impl() : floating_type_impl{"org.apache.cassandra.db.marshal.DoubleType"} { } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::double_; } }; struct float_type_impl : floating_type_impl { float_type_impl() : floating_type_impl{"org.apache.cassandra.db.marshal.FloatType"} { } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::float_; } }; struct empty_type_impl : abstract_type { empty_type_impl() : abstract_type("org.apache.cassandra.db.marshal.EmptyType") {} virtual void serialize(const boost::any& value, bytes::iterator& out) const override { } virtual size_t serialized_size(const boost::any& value) const override { return 0; } virtual bool less(bytes_view v1, bytes_view v2) const override { return false; } virtual size_t hash(bytes_view v) const override { return 0; } virtual boost::any deserialize(bytes_view v) const override { return {}; } virtual sstring to_string(const bytes& b) const override { // FIXME: abort(); } virtual bytes from_string(sstring_view text) const override { // FIXME: abort(); } virtual shared_ptr as_cql3_type() const override { // Can't happen abort(); } }; logging::logger collection_type_impl::_logger("collection_type_impl"); const size_t collection_type_impl::max_elements; const collection_type_impl::kind collection_type_impl::kind::map( [] (shared_ptr collection, bool is_key) -> shared_ptr { // FIXME: implement // return isKey ? Maps.keySpecOf(collection) : Maps.valueSpecOf(collection); abort(); }); const collection_type_impl::kind collection_type_impl::kind::set( [] (shared_ptr collection, bool is_key) -> shared_ptr { // FIXME: implement // return Sets.valueSpecOf(collection); abort(); }); const collection_type_impl::kind collection_type_impl::kind::list( [] (shared_ptr collection, bool is_key) -> shared_ptr { // FIXME: implement // return Lists.valueSpecOf(collection); abort(); }); shared_ptr collection_type_impl::kind::make_collection_receiver(shared_ptr collection, bool is_key) const { return _impl(std::move(collection), is_key); } shared_ptr collection_type_impl::make_collection_receiver(shared_ptr collection, bool is_key) const { return _kind.make_collection_receiver(std::move(collection), is_key); } std::vector collection_type_impl::enforce_limit(std::vector cells, int version) const { assert(is_multi_cell()); if (version >= 3 || cells.size() <= max_elements) { return cells; } _logger.error("Detected collection with {} elements, more than the {} limit. Only the first {} elements will be returned to the client. " "Please see http://cassandra.apache.org/doc/cql3/CQL.html#collections for more details.", cells.size(), max_elements, max_elements); cells.erase(cells.begin() + max_elements, cells.end()); return cells; } bytes collection_type_impl::serialize_for_native_protocol(std::vector cells, int version) const { assert(is_multi_cell()); cells = enforce_limit(std::move(cells), version); std::vector values = serialized_values(std::move(cells)); // FIXME: implement abort(); // return CollectionSerializer.pack(values, cells.size(), version); } bool collection_type_impl::is_compatible_with(const abstract_type& previous) const { // FIXME: implement abort(); } shared_ptr collection_type_impl::as_cql3_type() const { if (!_cql3_type) { auto name = cql3_type_name(); if (!is_multi_cell()) { name = "frozen<" + name + ">"; } _cql3_type = make_shared(name, shared_from_this(), false); } return _cql3_type; } bytes collection_type_impl::to_value(collection_mutation::view mut, serialization_format sf) const { return to_value(deserialize_mutation_form(mut), sf); } collection_type_impl::mutation collection_type_impl::mutation_view::materialize() const { collection_type_impl::mutation m; m.tomb = tomb; m.cells.reserve(cells.size()); for (auto&& e : cells) { m.cells.emplace_back(bytes(e.first.begin(), e.first.end()), e.second); } return m; } size_t collection_size_len(serialization_format sf) { if (sf.using_32_bits_for_collections()) { return sizeof(int32_t); } return sizeof(uint16_t); } size_t collection_value_len(serialization_format sf) { if (sf.using_32_bits_for_collections()) { return sizeof(int32_t); } return sizeof(uint16_t); } int read_collection_size(bytes_view& in, serialization_format sf) { if (sf.using_32_bits_for_collections()) { return read_simple(in); } else { return read_simple(in); } } void write_collection_size(bytes::iterator& out, int size, serialization_format sf) { if (sf.using_32_bits_for_collections()) { serialize_int32(out, size); } else { serialize_int16(out, uint16_t(size)); } } bytes_view read_collection_value(bytes_view& in, serialization_format sf) { auto size = sf.using_32_bits_for_collections() ? read_simple(in) : read_simple(in); return read_simple_bytes(in, size); } void write_collection_value(bytes::iterator& out, serialization_format sf, bytes_view val_bytes) { if (sf.using_32_bits_for_collections()) { serialize_int32(out, int32_t(val_bytes.size())); } else { serialize_int16(out, uint16_t(val_bytes.size())); } out = std::copy_n(val_bytes.begin(), val_bytes.size(), out); } void write_collection_value(bytes::iterator& out, serialization_format sf, data_type type, const boost::any& value) { size_t val_len = type->serialized_size(value); if (sf.using_32_bits_for_collections()) { serialize_int32(out, val_len); } else { serialize_int16(out, val_len); } type->serialize(value, out); } map_type map_type_impl::get_instance(data_type keys, data_type values, bool is_multi_cell) { return intern::get_instance(std::move(keys), std::move(values), is_multi_cell); } map_type_impl::map_type_impl(data_type keys, data_type values, bool is_multi_cell) : collection_type_impl("org.apache.cassandra.db.marshal.MapType(" + keys->name() + "," + values->name() + ")", kind::map) , _keys(std::move(keys)) , _values(std::move(values)) , _is_multi_cell(is_multi_cell) { } data_type map_type_impl::freeze() const { if (_is_multi_cell) { return get_instance(_keys, _values, false); } else { return shared_from_this(); } } bool map_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const { assert(!_is_multi_cell); auto* p = dynamic_cast(&previous); if (!p) { return false; } return _keys->is_compatible_with(*p->_keys) && _values->is_compatible_with(*p->_values); } bool map_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const { assert(!_is_multi_cell); auto* p = dynamic_cast(&previous); if (!p) { return false; } return _keys->is_compatible_with(*p->_keys) && _values->is_value_compatible_with(*p->_values); } bool map_type_impl::less(bytes_view o1, bytes_view o2) const { return compare_maps(_keys, _values, o1, o2) < 0; } int32_t map_type_impl::compare_maps(data_type keys, data_type values, bytes_view o1, bytes_view o2) { if (o1.empty()) { return o2.empty() ? 0 : -1; } else if (o2.empty()) { return 1; } auto sf = serialization_format::internal(); int size1 = read_collection_size(o1, sf); int size2 = read_collection_size(o2, sf); // FIXME: use std::lexicographical_compare() for (int i = 0; i < std::min(size1, size2); ++i) { auto k1 = read_collection_value(o1, sf); auto k2 = read_collection_value(o2, sf); auto cmp = keys->compare(k1, k2); if (cmp != 0) { return cmp; } auto v1 = read_collection_value(o1, sf); auto v2 = read_collection_value(o2, sf); cmp = values->compare(v1, v2); if (cmp != 0) { return cmp; } } return size1 == size2 ? 0 : (size1 < size2 ? -1 : 1); } void map_type_impl::serialize(const boost::any& value, bytes::iterator& out) const { return serialize(value, out, serialization_format::internal()); } size_t map_type_impl::serialized_size(const boost::any& value) const { auto& m = boost::any_cast(value); size_t len = collection_size_len(serialization_format::internal()); size_t psz = collection_value_len(serialization_format::internal()); for (auto&& kv : m) { len += psz + _keys->serialized_size(kv.first); len += psz + _values->serialized_size(kv.second); } return len; } void map_type_impl::serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const { auto& m = boost::any_cast(value); write_collection_size(out, m.size(), sf); for (auto&& kv : m) { write_collection_value(out, sf, _keys, kv.first); write_collection_value(out, sf, _values, kv.second); } } boost::any map_type_impl::deserialize(bytes_view v) const { return deserialize(v, serialization_format::internal()); } boost::any map_type_impl::deserialize(bytes_view in, serialization_format sf) const { if (in.empty()) { return {}; } native_type m; auto size = read_collection_size(in, sf); for (int i = 0; i < size; ++i) { auto kb = read_collection_value(in, sf); auto k = _keys->deserialize(kb); auto vb = read_collection_value(in, sf); auto v = _values->deserialize(vb); m.insert(m.end(), std::make_pair(std::move(k), std::move(v))); } return { std::move(m) }; } sstring map_type_impl::to_string(const bytes& b) const { // FIXME: abort(); } size_t map_type_impl::hash(bytes_view v) const { // FIXME: abort(); } bytes map_type_impl::from_string(sstring_view text) const { // FIXME: abort(); } std::vector map_type_impl::serialized_values(std::vector cells) const { // FIXME: abort(); } bytes map_type_impl::to_value(mutation_view mut, serialization_format sf) const { std::vector tmp; tmp.reserve(mut.cells.size() * 2); for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb)) { tmp.emplace_back(e.first); tmp.emplace_back(e.second.value()); } } return pack(tmp.begin(), tmp.end(), tmp.size() / 2, sf); } bytes map_type_impl::serialize_partially_deserialized_form( const std::vector>& v, serialization_format sf) { size_t len = collection_value_len(sf) * v.size() * 2 + collection_size_len(sf); for (auto&& e : v) { len += e.first.size() + e.second.size(); } bytes b(bytes::initialized_later(), len); bytes::iterator out = b.begin(); write_collection_size(out, v.size(), sf); for (auto&& e : v) { write_collection_value(out, sf, e.first); write_collection_value(out, sf, e.second); } return b; } sstring map_type_impl::cql3_type_name() const { return sprint("map<%s, %s>", _keys->as_cql3_type(), _values->as_cql3_type()); } auto collection_type_impl::deserialize_mutation_form(collection_mutation::view cm) const -> mutation_view { auto&& in = cm.data; mutation_view ret; auto has_tomb = read_simple(in); if (has_tomb) { auto ts = read_simple(in); auto ttl = read_simple(in); ret.tomb = tombstone{ts, gc_clock::time_point(gc_clock::duration(ttl))}; } auto nr = read_simple(in); ret.cells.reserve(nr); for (uint32_t i = 0; i != nr; ++i) { // FIXME: we could probably avoid the need for size auto ksize = read_simple(in); auto key = read_simple_bytes(in, ksize); auto vsize = read_simple(in); auto value = atomic_cell_view::from_bytes(read_simple_bytes(in, vsize)); ret.cells.emplace_back(key, value); } assert(in.empty()); return ret; } bool collection_type_impl::is_empty(collection_mutation::view cm) const { auto&& in = cm.data; auto has_tomb = read_simple(in); if (has_tomb) { in.remove_prefix(sizeof(api::timestamp_type) + sizeof(gc_clock::duration::rep)); } return read_simple(in) == 0; } bool collection_type_impl::is_any_live(collection_mutation::view cm, tombstone tomb, gc_clock::time_point now) const { auto&& in = cm.data; auto has_tomb = read_simple(in); if (has_tomb) { auto ts = read_simple(in); auto ttl = read_simple(in); tomb.apply(tombstone{ts, gc_clock::time_point(gc_clock::duration(ttl))}); } auto nr = read_simple(in); for (uint32_t i = 0; i != nr; ++i) { auto ksize = read_simple(in); in.remove_prefix(ksize); auto vsize = read_simple(in); auto value = atomic_cell_view::from_bytes(read_simple_bytes(in, vsize)); if (value.is_live(tomb, now)) { return true; } } return false; } template collection_mutation::one do_serialize_mutation_form( std::experimental::optional tomb, boost::iterator_range cells) { auto element_size = [] (size_t c, auto&& e) -> size_t { return c + 8 + e.first.size() + e.second.serialize().size(); }; auto size = accumulate(cells, (size_t)4, element_size); size += 1; if (tomb) { size += sizeof(tomb->timestamp) + sizeof(tomb->deletion_time); } bytes ret(bytes::initialized_later(), size); bytes::iterator out = ret.begin(); *out++ = bool(tomb); if (tomb) { write(out, tomb->timestamp); write(out, tomb->deletion_time.time_since_epoch().count()); } auto writeb = [&out] (bytes_view v) { serialize_int32(out, v.size()); out = std::copy_n(v.begin(), v.size(), out); }; // FIXME: overflow? serialize_int32(out, boost::distance(cells)); for (auto&& kv : cells) { auto&& k = kv.first; auto&& v = kv.second; writeb(k); writeb(v.serialize()); } return collection_mutation::one{std::move(ret)}; } bool collection_type_impl::mutation::compact_and_expire(tombstone base_tomb, gc_clock::time_point query_time) { bool any_live = false; tomb.apply(base_tomb); std::vector> survivors; for (auto&& name_and_cell : cells) { atomic_cell& cell = name_and_cell.second; if (cell.is_covered_by(tomb)) { continue; } if (cell.has_expired(query_time)) { survivors.emplace_back(std::make_pair( std::move(name_and_cell.first), atomic_cell::make_dead(cell.timestamp(), cell.deletion_time()))); } else { any_live |= cell.is_live(); survivors.emplace_back(std::move(name_and_cell)); } } cells = std::move(survivors); return any_live; } collection_mutation::one collection_type_impl::serialize_mutation_form(const mutation& mut) const { return do_serialize_mutation_form(mut.tomb, boost::make_iterator_range(mut.cells.begin(), mut.cells.end())); } collection_mutation::one collection_type_impl::serialize_mutation_form(mutation_view mut) const { return do_serialize_mutation_form(mut.tomb, boost::make_iterator_range(mut.cells.begin(), mut.cells.end())); } collection_mutation::one collection_type_impl::serialize_mutation_form_only_live(mutation_view mut, gc_clock::time_point now) const { return do_serialize_mutation_form(mut.tomb, mut.cells | boost::adaptors::filtered([t = mut.tomb, now] (auto&& e) { return e.second.is_live(t, now); })); } collection_mutation::one collection_type_impl::merge(collection_mutation::view a, collection_mutation::view b) const { auto aa = deserialize_mutation_form(a); auto bb = deserialize_mutation_form(b); mutation_view merged; merged.cells.reserve(aa.cells.size() + bb.cells.size()); using element_type = std::pair; auto key_type = name_comparator(); auto compare = [key_type] (const element_type& e1, const element_type& e2) { return key_type->less(e1.first, e2.first); }; auto merge = [this] (const element_type& e1, const element_type& e2) { // FIXME: use std::max()? return std::make_pair(e1.first, compare_atomic_cell_for_merge(e1.second, e2.second) > 0 ? e1.second : e2.second); }; // applied to a tombstone, returns a predicate checking whether a cell is killed by // the tombstone auto cell_killed = [] (const std::experimental::optional& t) { return [&t] (const element_type& e) { if (!t) { return false; } // tombstone wins if timestamps equal here, unlike row tombstones if (t->timestamp < e.second.timestamp()) { return false; } return true; // FIXME: should we consider TTLs too? }; }; combine(aa.cells.begin(), std::remove_if(aa.cells.begin(), aa.cells.end(), cell_killed(bb.tomb)), bb.cells.begin(), std::remove_if(bb.cells.begin(), bb.cells.end(), cell_killed(aa.tomb)), std::back_inserter(merged.cells), compare, merge); merged.tomb = std::max(aa.tomb, bb.tomb); return serialize_mutation_form(merged); } bytes_opt collection_type_impl::reserialize(serialization_format from, serialization_format to, bytes_view_opt v) const { if (!v) { return std::experimental::nullopt; } auto val = deserialize(*v, from); bytes ret(bytes::initialized_later(), serialized_size(v)); // FIXME: serialized_size want @to auto out = ret.begin(); serialize(std::move(val), out, to); return ret; } // iterator that takes a set or list in serialized form, and emits // each element, still in serialized form class listlike_partial_deserializing_iterator : public std::iterator { bytes_view* _in; int _remain; bytes_view _cur; serialization_format _sf; private: struct end_tag {}; listlike_partial_deserializing_iterator(bytes_view& in, serialization_format sf) : _in(&in), _sf(sf) { _remain = read_collection_size(*_in, _sf); parse(); } listlike_partial_deserializing_iterator(end_tag) : _remain(0), _sf(serialization_format::internal()) { // _sf is bogus, but doesn't matter } public: bytes_view operator*() const { return _cur; } listlike_partial_deserializing_iterator& operator++() { --_remain; parse(); return *this; } void operator++(int) { --_remain; parse(); } bool operator==(const listlike_partial_deserializing_iterator& x) const { return _remain == x._remain; } bool operator!=(const listlike_partial_deserializing_iterator& x) const { return _remain != x._remain; } static listlike_partial_deserializing_iterator begin(bytes_view& in, serialization_format sf) { return { in, sf }; } static listlike_partial_deserializing_iterator end(bytes_view in, serialization_format sf) { return { end_tag() }; } private: void parse() { if (_remain) { _cur = read_collection_value(*_in, _sf); } else { _cur = {}; } } }; set_type set_type_impl::get_instance(data_type elements, bool is_multi_cell) { return intern::get_instance(elements, is_multi_cell); } set_type_impl::set_type_impl(data_type elements, bool is_multi_cell) : collection_type_impl("org.apache.cassandra.db.marshal.SetType(" + elements->name() + ")", kind::set) , _elements(std::move(elements)) , _is_multi_cell(is_multi_cell) { } data_type set_type_impl::value_comparator() const { return empty_type; } data_type set_type_impl::freeze() const { if (_is_multi_cell) { return get_instance(_elements, false); } else { return shared_from_this(); } } bool set_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const { assert(!_is_multi_cell); auto* p = dynamic_cast(&previous); if (!p) { return false; } return _elements->is_compatible_with(*p->_elements); } bool set_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const { return is_compatible_with(previous); } bool set_type_impl::less(bytes_view o1, bytes_view o2) const { using llpdi = listlike_partial_deserializing_iterator; auto sf = serialization_format::internal(); return std::lexicographical_compare( llpdi::begin(o1, sf), llpdi::end(o1, sf), llpdi::begin(o2, sf), llpdi::end(o2, sf), [this] (bytes_view o1, bytes_view o2) { return _elements->less(o1, o2); }); } void set_type_impl::serialize(const boost::any& value, bytes::iterator& out) const { return serialize(value, out, serialization_format::internal()); } size_t set_type_impl::serialized_size(const boost::any& value) const { auto& s = boost::any_cast(value); size_t len = collection_size_len(serialization_format::internal()); size_t psz = collection_value_len(serialization_format::internal()); for (auto&& e : s) { len += psz + _elements->serialized_size(e); } return len; } void set_type_impl::serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const { auto& s = boost::any_cast(value); write_collection_size(out, s.size(), sf); for (auto&& e : s) { write_collection_value(out, sf, _elements, e); } } boost::any set_type_impl::deserialize(bytes_view in) const { return deserialize(in, serialization_format::internal()); } boost::any set_type_impl::deserialize(bytes_view in, serialization_format sf) const { if (in.empty()) { return {}; } auto nr = read_collection_size(in, sf); native_type s; s.reserve(nr); for (int i = 0; i != nr; ++i) { auto e = _elements->deserialize(read_collection_value(in, sf)); if (e.empty()) { throw marshal_exception(); } s.push_back(std::move(e)); } return { s }; } sstring set_type_impl::to_string(const bytes& b) const { using llpdi = listlike_partial_deserializing_iterator; std::ostringstream out; bool first = true; auto v = bytes_view(b); auto sf = serialization_format::internal(); std::for_each(llpdi::begin(v, sf), llpdi::end(v, sf), [&first, &out, this] (bytes_view e) { if (first) { first = false; } else { out << "; "; } out << _elements->to_string(bytes(e.begin(), e.end())); }); return out.str(); } size_t set_type_impl::hash(bytes_view v) const { // FIXME: abort(); } bytes set_type_impl::from_string(sstring_view text) const { // FIXME: abort(); } std::vector set_type_impl::serialized_values(std::vector cells) const { // FIXME: abort(); } bytes set_type_impl::to_value(mutation_view mut, serialization_format sf) const { std::vector tmp; tmp.reserve(mut.cells.size()); for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb)) { tmp.emplace_back(e.first); } } return pack(tmp.begin(), tmp.end(), tmp.size(), sf); } bytes set_type_impl::serialize_partially_deserialized_form( const std::vector& v, serialization_format sf) const { return pack(v.begin(), v.end(), v.size(), sf); } sstring set_type_impl::cql3_type_name() const { return sprint("set<%s>", _elements->as_cql3_type()); } list_type list_type_impl::get_instance(data_type elements, bool is_multi_cell) { return intern::get_instance(elements, is_multi_cell); } list_type_impl::list_type_impl(data_type elements, bool is_multi_cell) : collection_type_impl("org.apache.cassandra.db.marshal.ListType(" + elements->name() + ")", kind::list) , _elements(std::move(elements)) , _is_multi_cell(is_multi_cell) { } data_type list_type_impl::name_comparator() const { return timeuuid_type; } data_type list_type_impl::value_comparator() const { return _elements; } data_type list_type_impl::freeze() const { if (_is_multi_cell) { return get_instance(_elements, false); } else { return shared_from_this(); } } bool list_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const { assert(!_is_multi_cell); auto* p = dynamic_cast(&previous); if (!p) { return false; } return _elements->is_compatible_with(*p->_elements); } bool list_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const { auto& lp = dynamic_cast(previous); return _elements->is_value_compatible_with_internal(*lp._elements); } bool list_type_impl::less(bytes_view o1, bytes_view o2) const { using llpdi = listlike_partial_deserializing_iterator; auto sf = serialization_format::internal(); return std::lexicographical_compare( llpdi::begin(o1, sf), llpdi::end(o1, sf), llpdi::begin(o2, sf), llpdi::end(o2, sf), [this] (bytes_view o1, bytes_view o2) { return _elements->less(o1, o2); }); } void list_type_impl::serialize(const boost::any& value, bytes::iterator& out) const { return serialize(value, out, serialization_format::internal()); } void list_type_impl::serialize(const boost::any& value, bytes::iterator& out, serialization_format sf) const { auto& s = boost::any_cast(value); write_collection_size(out, s.size(), sf); for (auto&& e : s) { write_collection_value(out, sf, _elements, e); } } size_t list_type_impl::serialized_size(const boost::any& value) const { auto& s = boost::any_cast(value); size_t len = collection_size_len(serialization_format::internal()); size_t psz = collection_value_len(serialization_format::internal()); for (auto&& e : s) { len += psz + _elements->serialized_size(e); } return len; } boost::any list_type_impl::deserialize(bytes_view in) const { return deserialize(in, serialization_format::internal()); } boost::any list_type_impl::deserialize(bytes_view in, serialization_format sf) const { if (in.empty()) { return {}; } auto nr = read_collection_size(in, sf); native_type s; s.reserve(nr); for (int i = 0; i != nr; ++i) { auto e = _elements->deserialize(read_collection_value(in, sf)); if (e.empty()) { throw marshal_exception(); } s.push_back(std::move(e)); } return { s }; } sstring list_type_impl::to_string(const bytes& b) const { using llpdi = listlike_partial_deserializing_iterator; std::ostringstream out; bool first = true; auto v = bytes_view(b); auto sf = serialization_format::internal(); std::for_each(llpdi::begin(v, sf), llpdi::end(v, sf), [&first, &out, this] (bytes_view e) { if (first) { first = false; } else { out << ", "; } out << _elements->to_string(bytes(e.begin(), e.end())); }); return out.str(); } size_t list_type_impl::hash(bytes_view v) const { // FIXME: abort(); } bytes list_type_impl::from_string(sstring_view text) const { // FIXME: abort(); } std::vector list_type_impl::serialized_values(std::vector cells) const { // FIXME: abort(); } bytes list_type_impl::to_value(mutation_view mut, serialization_format sf) const { std::vector tmp; tmp.reserve(mut.cells.size()); for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb)) { tmp.emplace_back(e.second.value()); } } return pack(tmp.begin(), tmp.end(), tmp.size(), sf); } sstring list_type_impl::cql3_type_name() const { return sprint("list<%s>", _elements->as_cql3_type()); } tuple_type_impl::tuple_type_impl(sstring name, std::vector types) : abstract_type(std::move(name)), _types(std::move(types)) { for (auto& t : _types) { t = t->freeze(); } } tuple_type_impl::tuple_type_impl(std::vector types) : tuple_type_impl(make_name(types), std::move(types)) { } shared_ptr tuple_type_impl::get_instance(std::vector types) { return ::make_shared(std::move(types)); } int32_t tuple_type_impl::compare(bytes_view v1, bytes_view v2) const { return lexicographical_tri_compare(_types.begin(), _types.end(), tuple_deserializing_iterator::start(v1), tuple_deserializing_iterator::finish(v1), tuple_deserializing_iterator::start(v2), tuple_deserializing_iterator::finish(v2), tri_compare_opt); } bool tuple_type_impl::less(bytes_view v1, bytes_view v2) const { return tuple_type_impl::compare(v1, v2) < 0; } size_t tuple_type_impl::serialized_size(const boost::any& value) const { size_t size = 0; if (value.empty()) { return size; } auto&& v = boost::any_cast(value); auto find_serialized_size = [] (auto&& t_v) { const data_type& t = boost::get<0>(t_v); const boost::any& v = boost::get<1>(t_v); return 4 + (v.empty() ? 0 : t->serialized_size(v)); }; return boost::accumulate(boost::combine(_types, v) | boost::adaptors::transformed(find_serialized_size), 0); } void tuple_type_impl::serialize(const boost::any& value, bytes::iterator& out) const { if (value.empty()) { return; } auto&& v = boost::any_cast(value); auto do_serialize = [&out] (auto&& t_v) { const data_type& t = boost::get<0>(t_v); const boost::any& v = boost::get<1>(t_v); if (v.empty()) { write(out, int32_t(-1)); } else { write(out, int32_t(t->serialized_size(v))); t->serialize(v, out); } }; boost::range::for_each(boost::combine(_types, v), do_serialize); } boost::any tuple_type_impl::deserialize(bytes_view v) const { native_type ret; ret.reserve(_types.size()); auto ti = _types.begin(); auto vi = tuple_deserializing_iterator::start(v); while (ti != _types.end() && vi != tuple_deserializing_iterator::finish(v)) { boost::any obj; if (*vi) { obj = (*ti)->deserialize(**vi); } ret.push_back(std::move(obj)); ++ti; ++vi; } ret.resize(_types.size()); return { ret }; } std::vector tuple_type_impl::split(bytes_view v) const { return { tuple_deserializing_iterator::start(v), tuple_deserializing_iterator::finish(v) }; } bytes tuple_type_impl::from_string(sstring_view s) const { throw std::runtime_error("not implemented"); } sstring tuple_type_impl::to_string(const bytes& b) const { throw std::runtime_error("not implemented"); } bool tuple_type_impl::equals(const abstract_type& other) const { auto x = dynamic_cast(&other); return x && std::equal(_types.begin(), _types.end(), x->_types.begin(), x->_types.end(), [] (auto&& a, auto&& b) { return a->equals(b); }); } bool tuple_type_impl::is_compatible_with(const abstract_type& previous) const { return check_compatibility(previous, &abstract_type::is_compatible_with); } bool tuple_type_impl::is_value_compatible_with_internal(const abstract_type& previous) const { return check_compatibility(previous, &abstract_type::is_value_compatible_with); } bool tuple_type_impl::check_compatibility(const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const) const { auto* x = dynamic_cast(&previous); if (!x) { return false; } auto c = std::mismatch( _types.begin(), _types.end(), x->_types.begin(), x->_types.end(), [predicate] (data_type a, data_type b) { return ((*a).*predicate)(*b); }); return c.first == _types.end(); // previous allowed to be longer } size_t tuple_type_impl::hash(bytes_view v) const { auto apply_hash = [] (auto&& type_value) { auto&& type = boost::get<0>(type_value); auto&& value = boost::get<1>(type_value); return value ? type->hash(*value) : 0; }; // FIXME: better accumulation function return boost::accumulate(combine(_types, make_range(v)) | boost::adaptors::transformed(apply_hash), 0, std::bit_xor<>()); } shared_ptr tuple_type_impl::as_cql3_type() const { return cql3::make_cql3_tuple_type(static_pointer_cast(shared_from_this())); } sstring tuple_type_impl::make_name(const std::vector& types) { return sprint("tuple<%s>", ::join(", ", types | boost::adaptors::transformed(std::mem_fn(&abstract_type::name)))); } sstring user_type_impl::get_name_as_string() const { return boost::any_cast(utf8_type->deserialize(_name)); } shared_ptr user_type_impl::as_cql3_type() const { throw "not yet"; } sstring user_type_impl::make_name(sstring keyspace, bytes name, std::vector field_names, std::vector field_types) { std::ostringstream os; os << "(" << keyspace << "," << to_hex(name); for (size_t i = 0; i < field_names.size(); ++i) { os << ","; os << to_hex(field_names[i]) << ":"; os << field_types[i]->name(); // FIXME: ignore frozen<> } os << ")"; return os.str(); } thread_local const shared_ptr int32_type(make_shared()); thread_local const shared_ptr long_type(make_shared()); thread_local const shared_ptr ascii_type(make_shared("org.apache.cassandra.db.marshal.AsciiType", [] { return cql3::cql3_type::ascii; })); thread_local const shared_ptr bytes_type(make_shared()); thread_local const shared_ptr utf8_type(make_shared("org.apache.cassandra.db.marshal.UTF8Type", [] { return cql3::cql3_type::text; })); thread_local const shared_ptr boolean_type(make_shared()); thread_local const shared_ptr date_type(make_shared()); thread_local const shared_ptr timeuuid_type(make_shared()); thread_local const shared_ptr timestamp_type(make_shared()); thread_local const shared_ptr uuid_type(make_shared()); thread_local const shared_ptr inet_addr_type(make_shared()); thread_local const shared_ptr float_type(make_shared()); thread_local const shared_ptr double_type(make_shared()); thread_local const data_type empty_type(make_shared()); data_type abstract_type::parse_type(const sstring& name) { static thread_local const std::unordered_map types = { { "org.apache.cassandra.db.marshal.Int32Type", int32_type }, { "org.apache.cassandra.db.marshal.LongType", long_type }, { "org.apache.cassandra.db.marshal.AsciiType", ascii_type }, { "org.apache.cassandra.db.marshal.BytesType", bytes_type }, { "org.apache.cassandra.db.marshal.UTF8Type", utf8_type }, { "org.apache.cassandra.db.marshal.BooleanType", boolean_type }, { "org.apache.cassandra.db.marshal.TimeUUIDType", timeuuid_type }, { "org.apache.cassandra.db.marshal.TimestampType", timestamp_type }, { "org.apache.cassandra.db.marshal.UUIDType", uuid_type }, { "org.apache.cassandra.db.marshal.InetAddressType", inet_addr_type }, { "org.apache.cassandra.db.marshal.FloatType", float_type }, { "org.apache.cassandra.db.marshal.DoubleType", double_type }, { "org.apache.cassandra.db.marshal.EmptyType", empty_type }, }; auto it = types.find(name); if (it == types.end()) { throw std::invalid_argument(sprint("unknown type: %s\n", name)); } return it->second; }