/* * Copyright (C) 2015-present ScyllaDB */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 */ #include #include #include "cql3/cql3_type.hh" #include "cql3/description.hh" #include "cql3/lists.hh" #include "cql3/maps.hh" #include "cql3/sets.hh" #include "cql3/util.hh" #include "concrete_types.hh" #include #include #include #include #include "types/types.hh" #include "utils/assert.hh" #include "utils/serialization.hh" #include "vint-serialization.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils/big_decimal.hh" #include "utils/date.h" #include "utils/utf8.hh" #include "utils/ascii.hh" #include "utils/fragment_range.hh" #include "utils/managed_bytes.hh" #include "utils/managed_string.hh" #include "types/user.hh" #include "types/tuple.hh" #include "types/vector.hh" #include "types/collection.hh" #include "types/map.hh" #include "types/list.hh" #include "types/set.hh" #include "types/listlike_partial_deserializing_iterator.hh" static logging::logger tlogger("types"); bytes_view_opt read_collection_value(bytes_view& in); void on_types_internal_error(std::exception_ptr ex) { on_internal_error(tlogger, std::move(ex)); } template requires requires { typename T::duration; requires std::same_as; } sstring time_point_to_string(const T& tp, bool use_time_separator = true) { auto count = tp.time_since_epoch().count(); auto d = std::div(int64_t(count), int64_t(1000)); std::time_t seconds = d.quot; std::tm tm; if (!gmtime_r(&seconds, &tm)) { return fmt::format("{} milliseconds (out of range)", count); } auto millis = d.rem; // adjust seconds for time points earlier than posix epoch // to keep the fractional millis positive if (millis < 0) { millis += 1000; seconds--; gmtime_r(&seconds, &tm); } const auto time_separator = (use_time_separator) ? "T" : " "; auto year_digits = tm.tm_year >= -1900 ? 4 : 5; return fmt::format("{:-0{}d}-{:02d}-{:02d}{}{:02d}:{:02d}:{:02d}.{:03d}Z", tm.tm_year + 1900, year_digits, tm.tm_mon + 1, tm.tm_mday, time_separator, tm.tm_hour, tm.tm_min, tm.tm_sec, millis); } sstring simple_date_to_string(const uint32_t days_count) { date::days days{days_count - (1UL << 31)}; date::year_month_day ymd{date::local_days{days}}; std::ostringstream str; str << ymd; return std::move(str).str(); } sstring time_to_string(const int64_t nanoseconds_count) { std::string s; std::chrono::nanoseconds nanoseconds{nanoseconds_count}; fmt::format_to(std::back_inserter(s), "{:%H:%M:%S}", nanoseconds); return s; } static const char* byte_type_name = "org.apache.cassandra.db.marshal.ByteType"; static const char* short_type_name = "org.apache.cassandra.db.marshal.ShortType"; static const char* int32_type_name = "org.apache.cassandra.db.marshal.Int32Type"; static const char* long_type_name = "org.apache.cassandra.db.marshal.LongType"; static const char* ascii_type_name = "org.apache.cassandra.db.marshal.AsciiType"; static const char* utf8_type_name = "org.apache.cassandra.db.marshal.UTF8Type"; static const char* bytes_type_name = "org.apache.cassandra.db.marshal.BytesType"; static const char* boolean_type_name = "org.apache.cassandra.db.marshal.BooleanType"; static const char* timeuuid_type_name = "org.apache.cassandra.db.marshal.TimeUUIDType"; static const char* timestamp_type_name = "org.apache.cassandra.db.marshal.TimestampType"; static const char* date_type_name = "org.apache.cassandra.db.marshal.DateType"; static const char* simple_date_type_name = "org.apache.cassandra.db.marshal.SimpleDateType"; static const char* time_type_name = "org.apache.cassandra.db.marshal.TimeType"; static const char* uuid_type_name = "org.apache.cassandra.db.marshal.UUIDType"; static const char* inet_addr_type_name = "org.apache.cassandra.db.marshal.InetAddressType"; static const char* double_type_name = "org.apache.cassandra.db.marshal.DoubleType"; static const char* float_type_name = "org.apache.cassandra.db.marshal.FloatType"; static const char* varint_type_name = "org.apache.cassandra.db.marshal.IntegerType"; static const char* decimal_type_name = "org.apache.cassandra.db.marshal.DecimalType"; static const char* counter_type_name = "org.apache.cassandra.db.marshal.CounterColumnType"; static const char* duration_type_name = "org.apache.cassandra.db.marshal.DurationType"; static const char* empty_type_name = "org.apache.cassandra.db.marshal.EmptyType"; template struct simple_type_traits { static constexpr size_t serialized_size = sizeof(T); static T read_nonempty(managed_bytes_view v) { return read_simple_exactly(v); } }; template<> struct simple_type_traits { static constexpr size_t serialized_size = 1; static bool read_nonempty(managed_bytes_view v) { return read_simple_exactly(v) != 0; } }; template<> struct simple_type_traits { static constexpr size_t serialized_size = sizeof(uint64_t); static db_clock::time_point read_nonempty(managed_bytes_view v) { return db_clock::time_point(db_clock::duration(read_simple_exactly(v))); } }; template simple_type_impl::simple_type_impl(abstract_type::kind k, sstring name, std::optional value_length_if_fixed) : concrete_type(k, std::move(name), std::move(value_length_if_fixed)) {} template integer_type_impl::integer_type_impl( abstract_type::kind k, sstring name, std::optional value_length_if_fixed) : simple_type_impl(k, name, std::move(value_length_if_fixed)) {} template static bytes decompose_value(T v) { bytes b(bytes::initialized_later(), sizeof(v)); write_unaligned(b.begin(), net::hton(v)); return b; } template static T parse_int(const integer_type_impl& t, std::string_view s) { try { auto value64 = boost::lexical_cast(s.begin(), s.size()); auto value = static_cast(value64); if (value != value64) { throw marshal_exception(seastar::format("Value out of range for type {}: '{}'", t.name(), s)); } return static_cast(value); } catch (const boost::bad_lexical_cast& e) { throw marshal_exception(seastar::format("Invalid number format '{}'", s)); } } // Note that although byte_type is of a fixed size, // Cassandra (erroneously) treats it as a variable-size // so we have to pass disengaged optional for the value size byte_type_impl::byte_type_impl() : integer_type_impl{kind::byte, byte_type_name, {}} {} short_type_impl::short_type_impl() : integer_type_impl{kind::short_kind, short_type_name, {}} {} int32_type_impl::int32_type_impl() : integer_type_impl{kind::int32, int32_type_name, 4} {} long_type_impl::long_type_impl() : integer_type_impl{kind::long_kind, long_type_name, 8} {} string_type_impl::string_type_impl(kind k, sstring name) : concrete_type(k, name, {}) {} ascii_type_impl::ascii_type_impl() : string_type_impl(kind::ascii, ascii_type_name) {} utf8_type_impl::utf8_type_impl() : string_type_impl(kind::utf8, utf8_type_name) {} bytes_type_impl::bytes_type_impl() : concrete_type(kind::bytes, bytes_type_name, {}) {} boolean_type_impl::boolean_type_impl() : simple_type_impl(kind::boolean, boolean_type_name, 1) {} date_type_impl::date_type_impl() : concrete_type(kind::date, date_type_name, 8) {} timeuuid_type_impl::timeuuid_type_impl() : concrete_type( kind::timeuuid, timeuuid_type_name, 16) {} timestamp_type_impl::timestamp_type_impl() : simple_type_impl(kind::timestamp, timestamp_type_name, 8) {} static boost::posix_time::ptime get_time(const boost::smatch& sm) { // Unfortunately boost::date_time parsers are more strict with regards // to the expected date format than we need to be. auto year = boost::lexical_cast(sm[1]); auto month = boost::lexical_cast(sm[2]); auto day = boost::lexical_cast(sm[3]); boost::gregorian::date date(year, month, day); auto hour = sm[5].length() ? boost::lexical_cast(sm[5]) : 0; auto minute = sm[6].length() ? boost::lexical_cast(sm[6]) : 0; auto second = sm[8].length() ? boost::lexical_cast(sm[8]) : 0; boost::posix_time::time_duration time(hour, minute, second); if (sm[10].length()) { static constexpr auto milliseconds_string_length = 3; auto length = sm[10].length(); if (length > milliseconds_string_length) { throw marshal_exception(format("Milliseconds length exceeds expected ({:d})", length)); } auto value = boost::lexical_cast(sm[10]); while (length < milliseconds_string_length) { value *= 10; length++; } time += boost::posix_time::milliseconds(value); } return boost::posix_time::ptime(date, time); } static boost::posix_time::time_duration get_utc_offset(const std::string& s) { 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("Cannot get UTC offset for a timestamp"); } int64_t timestamp_from_string(std::string_view s) { try { 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; } static const boost::regex date_re("^(\\d{4})-(\\d+)-(\\d+)([ tT](\\d+):(\\d+)(:(\\d+)(\\.(\\d+))?)?)?"); boost::smatch dsm; if (!boost::regex_search(str, dsm, date_re)) { throw marshal_exception(seastar::format("Unable to parse timestamp from '{}'", str)); } auto t = get_time(dsm); auto tz = dsm.suffix().str(); const auto str_begin = tz.find_first_not_of(" "); bool is_tz_empty = str_begin == std::string::npos; std::string_view tz_trim = tz; if (!is_tz_empty) { tz_trim.remove_prefix(str_begin); const auto str_end = tz_trim.find_last_not_of(" "); tz_trim.remove_suffix(tz_trim.size() - str_end - 1); } static const boost::regex tz_re("([\\+-]\\d{2}:?(\\d{2})?)"); boost::smatch tsm; if (boost::regex_match(tz, tsm, tz_re)) { t -= get_utc_offset(tsm.str()); } else if (is_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 if (tz_trim != "z" && tz_trim != "utc" && tz_trim != "gmt") { throw marshal_exception(seastar::format("Unable to parse timezone '{}'", tz)); } return (t - boost::posix_time::from_time_t(0)).total_milliseconds(); } catch (const marshal_exception& me) { throw marshal_exception( seastar::format("unable to parse date '{}': {}", s, me.what())); } catch (...) { throw marshal_exception(seastar::format("unable to parse date '{}': {}", s, std::current_exception())); } } db_clock::time_point timestamp_type_impl::from_string_view(std::string_view s) { return db_clock::time_point(db_clock::duration(timestamp_from_string(s))); } simple_date_type_impl::simple_date_type_impl() : simple_type_impl{kind::simple_date, simple_date_type_name, {}} {} template requires requires (ConstIterator it) { { it[0] } -> std::same_as; } static date::year_month_day get_simple_date_time(const boost::match_results& sm) { auto year = boost::lexical_cast(sm[1]); auto month = boost::lexical_cast(sm[2]); auto day = boost::lexical_cast(sm[3]); return date::year_month_day{date::year{year}, date::month{month}, date::day{day}}; } static uint32_t serialize(std::string_view input, int64_t days) { if (days < std::numeric_limits::min()) { throw marshal_exception(seastar::format("Input date {} is less than min supported date -5877641-06-23", input)); } if (days > std::numeric_limits::max()) { throw marshal_exception(seastar::format("Input date {} is greater than max supported date 5881580-07-11", input)); } days += 1UL << 31; return static_cast(days); } uint32_t simple_date_type_impl::from_string_view(std::string_view s) { char* end; errno = 0; auto v = std::strtoll(s.begin(), &end, 10); if(end != s.end()) { static const boost::regex date_re("^(-?\\d+)-(\\d+)-(\\d+)"); boost::match_results dsm; if (!boost::regex_match(s.begin(), s.end(), dsm, date_re)) { throw marshal_exception(seastar::format("Unable to coerce '{}' to a formatted date (long)", s)); } auto t = get_simple_date_time(dsm); return serialize(s, date::local_days(t).time_since_epoch().count()); } if (errno == ERANGE) { throw marshal_exception(format("Unable to make unsigned int (for date) from {}", v)); } if (v < std::numeric_limits::min() || v > std::numeric_limits::max()) { throw marshal_exception(format("Unable to make unsigned int (for date) from {}", v)); } return v; } time_type_impl::time_type_impl() : simple_type_impl{kind::time, time_type_name, {}} {} int64_t time_type_impl::from_string_view(std::string_view s) { static auto format_error = "Timestamp format must be hh:mm:ss[.fffffffff]"; auto hours_end = s.find(':'); if (hours_end == std::string::npos) { throw marshal_exception(format_error); } int64_t hours = std::stol(sstring(s.substr(0, hours_end))); if (hours < 0 || hours >= 24) { throw marshal_exception(format("Hour out of bounds ({:d}).", hours)); } auto minutes_end = s.find(':', hours_end + 1); if (minutes_end == std::string::npos) { throw marshal_exception(format_error); } int64_t minutes = std::stol(sstring(s.substr(hours_end + 1, hours_end - minutes_end))); if (minutes < 0 || minutes >= 60) { throw marshal_exception(format("Minute out of bounds ({:d}).", minutes)); } auto seconds_end = s.find('.', minutes_end + 1); if (seconds_end == std::string::npos) { seconds_end = s.length(); } int64_t seconds = std::stol(sstring(s.substr(minutes_end + 1, minutes_end - seconds_end))); if (seconds < 0 || seconds >= 60) { throw marshal_exception(format("Second out of bounds ({:d}).", seconds)); } int64_t nanoseconds = 0; if (seconds_end < s.length()) { nanoseconds = std::stol(sstring(s.substr(seconds_end + 1))); auto nano_digits = s.length() - (seconds_end + 1); if (nano_digits > 9) { throw marshal_exception(seastar::format("more than 9 nanosecond digits: {}", s)); } nanoseconds *= std::pow(10, 9 - nano_digits); if (nanoseconds < 0 || nanoseconds >= 1000 * 1000 * 1000) { throw marshal_exception(format("Nanosecond out of bounds ({:d}).", nanoseconds)); } } std::chrono::nanoseconds result{}; result += std::chrono::hours(hours); result += std::chrono::minutes(minutes); result += std::chrono::seconds(seconds); result += std::chrono::nanoseconds(nanoseconds); return result.count(); } uuid_type_impl::uuid_type_impl() : concrete_type(kind::uuid, uuid_type_name, 16) {} using inet_address = seastar::net::inet_address; inet_addr_type_impl::inet_addr_type_impl() : concrete_type(kind::inet, inet_addr_type_name, {}) {} // 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 constexpr size_t serialized_size = sizeof(typename int_of_size::itype); static double read_nonempty(managed_bytes_view v) { return std::bit_cast(read_simple_exactly::itype>(v)); } }; template<> struct simple_type_traits : public float_type_traits {}; template<> struct simple_type_traits : public float_type_traits {}; template floating_type_impl::floating_type_impl( abstract_type::kind k, sstring name, std::optional value_length_if_fixed) : simple_type_impl(k, std::move(name), std::move(value_length_if_fixed)) {} double_type_impl::double_type_impl() : floating_type_impl{kind::double_kind, double_type_name, 8} {} float_type_impl::float_type_impl() : floating_type_impl{kind::float_kind, float_type_name, 4} {} varint_type_impl::varint_type_impl() : concrete_type{kind::varint, varint_type_name, { }} { } decimal_type_impl::decimal_type_impl() : concrete_type{kind::decimal, decimal_type_name, { }} { } counter_type_impl::counter_type_impl() : abstract_type{kind::counter, counter_type_name, {}} {} // TODO(jhaberku): Move this to Seastar. template auto generate_tuple_from_index(std::index_sequence, Function&& f) { // To ensure that tuple is constructed in the correct order (because the evaluation order of the arguments to // `std::make_tuple` is unspecified), use braced initialization (which does define the order). However, we still // need to figure out the type. using result_type = decltype(std::make_tuple(f(Ts)...)); return result_type{f(Ts)...}; } duration_type_impl::duration_type_impl() : concrete_type(kind::duration, duration_type_name, {}) {} using common_counter_type = cql_duration::common_counter_type; static std::tuple deserialize_counters(bytes_view v) { auto deserialize_and_advance = [&v] (auto&& i) { auto len = signed_vint::serialized_size_from_first_byte(v.front()); const auto d = signed_vint::deserialize(v); v.remove_prefix(len); if (v.empty() && (i != 2)) { throw marshal_exception("Cannot deserialize duration"); } return static_cast(d); }; return generate_tuple_from_index(std::make_index_sequence<3>(), std::move(deserialize_and_advance)); } empty_type_impl::empty_type_impl() : abstract_type(kind::empty, empty_type_name, 0) {} logging::logger collection_type_impl::_logger("collection_type_impl"); const size_t collection_type_impl::max_elements; lw_shared_ptr collection_type_impl::make_collection_receiver( const cql3::column_specification& collection, bool is_key) const { struct visitor { const cql3::column_specification& collection; bool is_key; lw_shared_ptr operator()(const abstract_type&) { abort(); } lw_shared_ptr operator()(const list_type_impl&) { return cql3::lists::value_spec_of(collection); } lw_shared_ptr operator()(const map_type_impl&) { return is_key ? cql3::maps::key_spec_of(collection) : cql3::maps::value_spec_of(collection); } lw_shared_ptr operator()(const set_type_impl&) { return cql3::sets::value_spec_of(collection); } }; return ::visit(*this, visitor{collection, is_key}); } listlike_collection_type_impl::listlike_collection_type_impl( kind k, sstring name, data_type elements, bool is_multi_cell) : collection_type_impl(k, name, is_multi_cell), _elements(elements) {} std::strong_ordering listlike_collection_type_impl::compare_with_map(const map_type_impl& map_type, bytes_view list, bytes_view map) const { SCYLLA_ASSERT((is_set() && map_type.get_keys_type() == _elements) || (!is_set() && map_type.get_values_type() == _elements)); if (list.empty()) { return map.empty() ? std::strong_ordering::equal : std::strong_ordering::less; } else if (map.empty()) { return std::strong_ordering::greater; } const abstract_type& element_type = *_elements; size_t list_size = read_collection_size(list); size_t map_size = read_collection_size(map); bytes_view_opt list_value; bytes_view_opt map_value[2]; // Lists are represented as vector>, sets are vector> size_t map_value_index = is_list(); // Both set elements and map keys are sorted, so can be compared in linear order; // List elements are stored in both vectors in list index order. for (size_t i = 0; i < std::min(list_size, map_size); ++i) { list_value = read_collection_value_nonnull(list); map_value[0] = read_collection_value_nonnull(map); // sets-as-maps happen to be serialized with NULL map_value[1] = read_collection_value(map); if (!list_value) { return std::strong_ordering::less; } // map_value[0] is known non-null, and sets will compare map_value[0]. auto cmp = element_type.compare(*list_value, *map_value[map_value_index]); if (cmp != 0) { return cmp; } } return list_size <=> map_size; } bytes listlike_collection_type_impl::serialize_map(const map_type_impl& map_type, const data_value& value) const { SCYLLA_ASSERT((is_set() && map_type.get_keys_type() == _elements) || (!is_set() && map_type.get_values_type() == _elements)); const std::vector>& map = map_type.from_value(value); // Lists are represented as vector>, sets are vector> bool first = is_set(); size_t len = collection_size_len(); size_t psz = collection_value_len(); for (const std::pair& entry : map) { len += psz + (first ? entry.first : entry.second).serialized_size(); } bytes b(bytes::initialized_later(), len); bytes::iterator out = b.begin(); write_collection_size(out, map.size()); for (const std::pair& entry : map) { write_collection_value(out, _elements, first ? entry.first : entry.second); } return b; } void listlike_collection_type_impl::validate_for_storage(const FragmentedView auto& value) const { for (auto val_opt : partially_deserialize_listlike(value)) { if (!val_opt) { throw exceptions::invalid_request_exception("Cannot store NULL in list or set"); } } } template void listlike_collection_type_impl::validate_for_storage(const managed_bytes_view& value) const; template void listlike_collection_type_impl::validate_for_storage(const fragmented_temporary_buffer::view& value) const; static bool is_compatible_with_aux(const collection_type_impl& t, const abstract_type& previous) { if (t.get_kind() != previous.get_kind()) { return false; } auto& cprev = static_cast(previous); if (t.is_multi_cell() != cprev.is_multi_cell()) { return false; } if (!t.is_multi_cell()) { return t.is_compatible_with_frozen(cprev); } if (!t.name_comparator()->is_compatible_with(*cprev.name_comparator())) { return false; } // the value comparator is only used for Cell values, so sorting doesn't matter return t.value_comparator()->is_value_compatible_with(*cprev.value_comparator()); } static bool is_value_compatible_with_internal_aux(const collection_type_impl& t, const abstract_type& previous) { if (t.get_kind() != previous.get_kind()) { return false; } auto& cprev = static_cast(previous); // for multi-cell collections, compatibility and value-compatibility are the same if (t.is_multi_cell() || cprev.is_multi_cell()) { return t.is_compatible_with(previous); } return t.is_value_compatible_with_frozen(cprev); } size_t collection_size_len() { return sizeof(int32_t); } size_t collection_value_len() { return sizeof(int32_t); } int read_collection_size(bytes_view& in) { return read_simple(in); } void write_collection_size(bytes::iterator& out, int size) { serialize_int32(out, size); } bytes_view read_collection_value_nonnull(bytes_view& in) { int32_t size = read_simple(in); if (size == -2) { throw exceptions::invalid_request_exception("unset value is not supported inside collections"); } if (size < 0) { throw exceptions::invalid_request_exception("null is not supported inside collections"); } return read_simple_bytes(in, size); } bytes_view read_collection_key(bytes_view& in) { int32_t size = read_simple(in); if (size < 0) { throw exceptions::invalid_request_exception("null/unset is not supported inside collections"); } return read_simple_bytes(in, size); } bytes_view_opt read_collection_value(bytes_view& in) { int32_t size = read_simple(in); if (size == -1) { throw std::nullopt; } if (size == -2) { throw exceptions::invalid_request_exception("unset value is not supported inside collections"); } if (size < 0) { throw exceptions::invalid_request_exception("null is not supported inside collections"); } return read_simple_bytes(in, size); } void write_collection_value(bytes::iterator& out, std::optional val_bytes_opt) { if (!val_bytes_opt) { serialize_int32(out, int32_t(-1)); return; } auto& val_bytes = *val_bytes_opt; serialize_int32(out, int32_t(val_bytes.size())); out = std::copy_n(val_bytes.begin(), val_bytes.size(), out); } // Passing the wrong integer type to a generic serialization function is a particularly // easy mistake to do, so we want to disable template parameter deduction here. // Hence std::type_identity. template void write_simple(bytes_ostream& out, std::type_identity_t val) { auto val_be = net::hton(val); auto val_ptr = reinterpret_cast(&val_be); out.write(bytes_view(val_ptr, sizeof(T))); } void write_collection_value(bytes_ostream& out, atomic_cell_value_view val) { write_simple(out, int32_t(val.size_bytes())); for (auto&& frag : fragment_range(val)) { out.write(frag); } } template void write_simple(managed_bytes_mutable_view& out, std::type_identity_t val) { val = net::hton(val); if (out.current_fragment().size() >= sizeof(T)) [[likely]] { auto p = out.current_fragment().data(); out.remove_prefix(sizeof(T)); // FIXME use write_unaligned after it's merged. write_unaligned(p, val); } else if (out.size_bytes() >= sizeof(T)) { write_fragmented(out, std::string_view(reinterpret_cast(&val), sizeof(T))); } else { on_internal_error(tlogger, format("write_simple: attempted write of size {} to buffer of size {}", sizeof(T), out.size_bytes())); } } void write_collection_size(managed_bytes_mutable_view& out, int size) { write_simple(out, uint32_t(size)); } void write_collection_value(managed_bytes_mutable_view& out, bytes_view val) { write_simple(out, int32_t(val.size())); write_fragmented(out, single_fragmented_view(val)); } void write_collection_value(managed_bytes_mutable_view& out, const managed_bytes_view_opt& val_opt) { if (!val_opt) { write_simple(out, int32_t(-1)); return; } auto& val = *val_opt; write_simple(out, int32_t(val.size_bytes())); write_fragmented(out, val); } void write_int32(bytes::iterator& out, int32_t value) { return serialize_int32(out, value); } shared_ptr abstract_type::underlying_type() const { struct visitor { shared_ptr operator()(const abstract_type& t) { return t.shared_from_this(); } shared_ptr operator()(const reversed_type_impl& r) { return r.underlying_type(); } }; return visit(*this, visitor{}); } bool abstract_type::is_counter() const { struct visitor { bool operator()(const reversed_type_impl& r) { return r.underlying_type()->is_counter(); } bool operator()(const abstract_type&) { return false; } bool operator()(const counter_type_impl&) { return true; } }; return visit(*this, visitor{}); } bool abstract_type::is_collection() const { struct visitor { bool operator()(const reversed_type_impl& r) { return r.underlying_type()->is_collection(); } bool operator()(const abstract_type&) { return false; } bool operator()(const collection_type_impl&) { return true; } }; return visit(*this, visitor{}); } bool abstract_type::is_tuple() const { struct visitor { bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_tuple(); } bool operator()(const tuple_type_impl&) { return true; } }; return visit(*this, visitor{}); } bool abstract_type::is_vector() const { struct visitor { bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_vector(); } bool operator()(const vector_type_impl&) { return true; } }; return visit(*this, visitor{}); } bool abstract_type::is_multi_cell() const { struct visitor { bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_multi_cell(); } bool operator()(const collection_type_impl& c) { return c.is_multi_cell(); } bool operator()(const user_type_impl& u) { return u.is_multi_cell(); } }; return visit(*this, visitor{}); } bool abstract_type::is_native() const { return !is_collection() && !is_tuple() && !is_vector(); } bool abstract_type::is_string() const { struct visitor { bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_string(); } bool operator()(const string_type_impl&) { return true; } }; return visit(*this, visitor{}); } template requires CanHandleAllTypes static bool find(const abstract_type& t, const Predicate& f) { struct visitor { const Predicate& f; bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& r) { return find(*r.underlying_type(), f); } bool operator()(const tuple_type_impl& t) { return std::ranges::any_of(t.all_types(), [&] (const data_type& dt) { return find(*dt, f); }); } bool operator()(const vector_type_impl& t) { return find(*t.get_elements_type(), f); } bool operator()(const map_type_impl& m) { return find(*m.get_keys_type(), f) || find(*m.get_values_type(), f); } bool operator()(const listlike_collection_type_impl& l) { return find(*l.get_elements_type(), f); } }; return visit(t, [&](const auto& t) { if (f(t)) { return true; } return visitor{f}(t); }); } bool abstract_type::references_duration() const { struct visitor { bool operator()(const abstract_type&) const { return false; } bool operator()(const duration_type_impl&) const { return true; } }; return find(*this, visitor{}); } bool abstract_type::references_user_type(const sstring& keyspace, const bytes& name) const { struct visitor { const sstring& keyspace; const bytes& name; bool operator()(const abstract_type&) const { return false; } bool operator()(const user_type_impl& u) const { return u._keyspace == keyspace && u._name == name; } }; return find(*this, visitor{keyspace, name}); } namespace { // For tuples and collection types: recurse over inner types // For user-defined types: add it's name to referenced udts // For other types: do nothing struct get_all_referenced_user_types_visitor { std::set referenced_udts; template void operator()(const concrete_type&) { return; } void operator()(const counter_type_impl&) { return; } void operator()(const empty_type_impl&) { return; } void operator()(const reversed_type_impl& r) { visit(*r.underlying_type(), *this); } void operator()(const user_type_impl& u) { referenced_udts.insert(::static_pointer_cast(u.shared_from_this())); } void operator()(const tuple_type_impl& t) { for (auto& field: t.all_types()) { visit(*field, *this); } } void operator()(const vector_type_impl& v) { visit(*v.get_elements_type(), *this); } void operator()(const map_type_impl& m) { visit(*m.get_keys_type(), *this); visit(*m.get_values_type(), *this); } void operator()(const listlike_collection_type_impl& l) { visit(*l.get_elements_type(), *this); } }; } std::set user_type_impl::get_all_referenced_user_types() const { get_all_referenced_user_types_visitor v; // cast this UDT to tuple to iterate over all fields v(static_cast(*this)); return std::move(v.referenced_udts); } namespace { struct is_byte_order_equal_visitor { template bool operator()(const simple_type_impl&) { return true; } bool operator()(const concrete_type&) { return true; } bool operator()(const abstract_type&) { return false; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_byte_order_equal(); } bool operator()(const string_type_impl&) { return true; } bool operator()(const bytes_type_impl&) { return true; } bool operator()(const timestamp_date_base_class&) { return true; } bool operator()(const inet_addr_type_impl&) { return true; } bool operator()(const duration_type_impl&) { return true; } // FIXME: origin returns false for list. Why? bool operator()(const set_type_impl& s) { return s.get_elements_type()->is_byte_order_equal(); } }; } bool abstract_type::is_byte_order_equal() const { return visit(*this, is_byte_order_equal_visitor{}); } static bool check_compatibility(const tuple_type_impl &t, const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const); static bool check_compatibility(const vector_type_impl &t, const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const); static bool is_fixed_size_int_type(const abstract_type& t) { using k = abstract_type::kind; switch (t.get_kind()) { case k::byte: case k::short_kind: case k::int32: case k::long_kind: return true; case k::ascii: case k::boolean: case k::bytes: case k::counter: case k::date: case k::decimal: case k::double_kind: case k::duration: case k::empty: case k::float_kind: case k::inet: case k::list: case k::map: case k::reversed: case k::set: case k::simple_date: case k::time: case k::timestamp: case k::timeuuid: case k::tuple: case k::user: case k::utf8: case k::uuid: case k::varint: case k::vector: return false; } __builtin_unreachable(); } bool abstract_type::is_compatible_with(const abstract_type& previous) const { if (this == &previous) { return true; } struct visitor { const abstract_type& previous; bool operator()(const reversed_type_impl& t) { if (previous.is_reversed()) { return t.underlying_type()->is_compatible_with(*previous.underlying_type()); } return false; } bool operator()(const utf8_type_impl&) { // Anything that is ascii is also utf8, and they both use bytes comparison return previous.is_string(); } bool operator()(const bytes_type_impl&) { // Both ascii_type_impl and utf8_type_impl really use bytes comparison and // bytesType validate everything, so it is compatible with the former. return previous.is_string(); } bool operator()(const date_type_impl& t) { if (previous.get_kind() == kind::timestamp) { static logging::logger date_logger(date_type_name); date_logger.warn("Changing from TimestampType to DateType is allowed, but be wary " "that they sort differently for pre-unix-epoch timestamps " "(negative timestamp values) and thus this change will corrupt " "your data if you have such negative timestamp. There is no " "reason to switch from DateType to TimestampType except if you " "were using DateType in the first place and switched to " "TimestampType by mistake."); return true; } return false; } bool operator()(const timestamp_type_impl& t) { if (previous.get_kind() == kind::date) { static logging::logger timestamp_logger(timestamp_type_name); timestamp_logger.warn("Changing from DateType to TimestampType is allowed, but be wary " "that they sort differently for pre-unix-epoch timestamps " "(negative timestamp values) and thus this change will corrupt " "your data if you have such negative timestamp. So unless you " "know that you don't have *any* pre-unix-epoch timestamp you " "should change back to DateType"); return true; } return false; } bool operator()(const tuple_type_impl& t) { return check_compatibility(t, previous, &abstract_type::is_compatible_with); } bool operator()(const vector_type_impl& t) { return check_compatibility(t, previous, &abstract_type::is_compatible_with); } bool operator()(const collection_type_impl& t) { return is_compatible_with_aux(t, previous); } bool operator()(const varint_type_impl& t) { return is_fixed_size_int_type(previous); } bool operator()(const abstract_type& t) { return false; } }; return visit(*this, visitor{previous}); } cql3::cql3_type abstract_type::as_cql3_type() const { return cql3::cql3_type(shared_from_this()); } static sstring cql3_type_name_impl(const abstract_type& t) { struct visitor { sstring operator()(const ascii_type_impl&) { return "ascii"; } sstring operator()(const boolean_type_impl&) { return "boolean"; } sstring operator()(const byte_type_impl&) { return "tinyint"; } sstring operator()(const bytes_type_impl&) { return "blob"; } sstring operator()(const counter_type_impl&) { return "counter"; } sstring operator()(const timestamp_date_base_class&) { return "timestamp"; } sstring operator()(const decimal_type_impl&) { return "decimal"; } sstring operator()(const double_type_impl&) { return "double"; } sstring operator()(const duration_type_impl&) { return "duration"; } sstring operator()(const empty_type_impl&) { return "empty"; } sstring operator()(const float_type_impl&) { return "float"; } sstring operator()(const inet_addr_type_impl&) { return "inet"; } sstring operator()(const int32_type_impl&) { return "int"; } sstring operator()(const list_type_impl& l) { return format("list<{}>", l.get_elements_type()->as_cql3_type()); } sstring operator()(const long_type_impl&) { return "bigint"; } sstring operator()(const map_type_impl& m) { return format("map<{}, {}>", m.get_keys_type()->as_cql3_type(), m.get_values_type()->as_cql3_type()); } sstring operator()(const reversed_type_impl& r) { return cql3_type_name_impl(*r.underlying_type()); } sstring operator()(const set_type_impl& s) { return format("set<{}>", s.get_elements_type()->as_cql3_type()); } sstring operator()(const short_type_impl&) { return "smallint"; } sstring operator()(const simple_date_type_impl&) { return "date"; } sstring operator()(const time_type_impl&) { return "time"; } sstring operator()(const timeuuid_type_impl&) { return "timeuuid"; } sstring operator()(const tuple_type_impl& t) { return seastar::format("tuple<{}>", fmt::join(t.all_types() | std::views::transform(std::mem_fn( &abstract_type::as_cql3_type)), ", ")); } sstring operator()(const user_type_impl& u) { return u.get_name_as_cql_string(); } sstring operator()(const utf8_type_impl&) { return "text"; } sstring operator()(const uuid_type_impl&) { return "uuid"; } sstring operator()(const varint_type_impl&) { return "varint"; } sstring operator()(const vector_type_impl& v) { return format("vector<{}, {}>", v.get_elements_type()->as_cql3_type(), v.get_dimension()); } }; return visit(t, visitor{}); } const sstring& abstract_type::cql3_type_name() const { if (_cql3_type_name.empty()) { auto name = cql3_type_name_impl(*this); if (!is_native() && !is_multi_cell() && !is_vector()) { name = "frozen<" + name + ">"; } _cql3_type_name = name; } return _cql3_type_name; } sstring abstract_type::cql3_type_name_without_frozen() const { return cql3_type_name_impl(*this); } void write_collection_value(bytes::iterator& out, data_type type, const data_value& value) { if (value.is_null()) { auto val_len = -1; serialize_int32(out, val_len); return; } size_t val_len = value.serialized_size(); serialize_int32(out, val_len); value.serialize(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); } sstring make_map_type_name(data_type keys, data_type values, bool is_multi_cell) { sstring ret = ""; if (!is_multi_cell) { ret = "org.apache.cassandra.db.marshal.FrozenType("; } ret += "org.apache.cassandra.db.marshal.MapType(" + keys->name() + "," + values->name() + ")"; if (!is_multi_cell) { ret += ")"; } return ret; } map_type_impl::map_type_impl(data_type keys, data_type values, bool is_multi_cell) : concrete_type(kind::map, make_map_type_name(keys, values, is_multi_cell), is_multi_cell) , _keys(std::move(keys)) , _values(std::move(values)) { _contains_set_or_map = true; } 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 { SCYLLA_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 { SCYLLA_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); } std::strong_ordering map_type_impl::compare_maps(data_type keys, data_type values, managed_bytes_view o1, managed_bytes_view o2) { if (o1.empty()) { return o2.empty() ? std::strong_ordering::equal : std::strong_ordering::less; } else if (o2.empty()) { return std::strong_ordering::greater; } int size1 = read_collection_size(o1); int size2 = read_collection_size(o2); // FIXME: use std::lexicographical_compare() for (int i = 0; i < std::min(size1, size2); ++i) { auto k1 = read_collection_key(o1); auto k2 = read_collection_key(o2); auto cmp = keys->compare(k1, k2); if (cmp != 0) { return cmp; } auto v1 = read_collection_value_nonnull(o1); auto v2 = read_collection_value_nonnull(o2); cmp = values->compare(v1, v2); if (cmp != 0) { return cmp; } } return size1 <=> size2; } static size_t map_serialized_size(const map_type_impl::native_type* m) { size_t len = collection_size_len(); size_t psz = collection_value_len(); for (auto&& kv : *m) { len += psz + kv.first.serialized_size(); len += psz + kv.second.serialized_size(); } return len; } static void serialize_map(const map_type_impl& t, const void* value, bytes::iterator& out) { auto& m = t.from_value(value); write_collection_size(out, m.size()); for (auto&& kv : m) { write_collection_value(out, t.get_keys_type(), kv.first); write_collection_value(out, t.get_values_type(), kv.second); } } template data_value map_type_impl::deserialize(View in) const { native_type m; auto size = read_collection_size(in); for (int i = 0; i < size; ++i) { auto k = _keys->deserialize(read_collection_key(in)); auto v = _values->deserialize(read_collection_value_nonnull(in)); m.insert(m.end(), std::make_pair(std::move(k), std::move(v))); } return make_value(std::move(m)); } template data_value map_type_impl::deserialize<>(ser::buffer_view) const; template data_value map_type_impl::deserialize<>(managed_bytes_view) const; template static void validate_aux(const map_type_impl& t, View v) { auto size = read_collection_size(v); for (int i = 0; i < size; ++i) { t.get_keys_type()->validate(read_collection_key(v)); t.get_values_type()->validate(read_collection_value_nonnull(v)); } } static sstring map_to_string(const std::vector>& v, bool include_frozen_type) { std::ostringstream out; if (include_frozen_type) { out << "("; } fmt::print(out, "{}", fmt::join(v | std::views::transform([] (const std::pair& p) { std::ostringstream out; const auto& k = p.first; const auto& v = p.second; out << "{" << k.type()->to_string_impl(k) << " : "; out << v.type()->to_string_impl(v) << "}"; return std::move(out).str(); }), ", ")); if (include_frozen_type) { out << ")"; } return std::move(out).str(); } bytes map_type_impl::serialize_partially_deserialized_form( const std::vector>& v) { size_t len = collection_value_len() * v.size() * 2 + collection_size_len(); 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()); for (auto&& e : v) { write_collection_value(out, e.first); write_collection_value(out, e.second); } return b; } managed_bytes map_type_impl::serialize_partially_deserialized_form_fragmented( const std::vector>& v) { size_t len = collection_value_len() * v.size() * 2 + collection_size_len(); for (auto&& e : v) { len += e.first.size() + e.second.size(); } managed_bytes b(managed_bytes::initialized_later(), len); managed_bytes_mutable_view out = b; write_collection_size(out, v.size()); for (auto&& e : v) { write_collection_value(out, e.first); write_collection_value(out, e.second); } return b; } static std::optional update_user_type_aux( const map_type_impl& m, const shared_ptr updated) { auto old_keys = m.get_keys_type(); auto old_values = m.get_values_type(); auto k = old_keys->update_user_type(updated); auto v = old_values->update_user_type(updated); if (!k && !v) { return std::nullopt; } return std::make_optional(static_pointer_cast( map_type_impl::get_instance(k ? *k : old_keys, v ? *v : old_values, m.is_multi_cell()))); } static void serialize(const abstract_type& t, const void* value, bytes::iterator& out); set_type set_type_impl::get_instance(data_type elements, bool is_multi_cell) { return intern::get_instance(elements, is_multi_cell); } sstring make_set_type_name(data_type elements, bool is_multi_cell) { sstring ret = ""; if (!is_multi_cell) { ret = "org.apache.cassandra.db.marshal.FrozenType("; } ret += "org.apache.cassandra.db.marshal.SetType(" + elements->name() + ")"; if (!is_multi_cell) { ret += ")"; } return ret; } set_type_impl::set_type_impl(data_type elements, bool is_multi_cell) : concrete_type(kind::set, make_set_type_name(elements, is_multi_cell), elements, is_multi_cell) { _contains_set_or_map = true; } 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 { SCYLLA_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); } template static void validate_aux(const set_type_impl& t, View v) { auto nr = read_collection_size(v); for (int i = 0; i != nr; ++i) { t.get_elements_type()->validate(read_collection_value_nonnull(v)); } } static size_t listlike_serialized_size(const std::vector* s) { size_t len = collection_size_len(); size_t psz = collection_value_len(); for (auto&& e : *s) { len += psz + e.serialized_size(); } return len; } static void serialize_set(const set_type_impl& t, const void* value, bytes::iterator& out) { auto& s = t.from_value(value); write_collection_size(out, s.size()); for (auto&& e : s) { write_collection_value(out, t.get_elements_type(), e); } } template data_value set_type_impl::deserialize(View in) const { auto nr = read_collection_size(in); native_type s; s.reserve(nr); for (int i = 0; i != nr; ++i) { auto e = _elements->deserialize(read_collection_value_nonnull(in)); if (e.is_null()) { throw marshal_exception("Cannot deserialize a set"); } s.push_back(std::move(e)); } return make_value(std::move(s)); } template data_value set_type_impl::deserialize<>(ser::buffer_view) const; bytes set_type_impl::serialize_partially_deserialized_form( const std::vector& v) { return pack(v.begin(), v.end(), v.size()); } managed_bytes set_type_impl::serialize_partially_deserialized_form_fragmented( const std::vector& v) { return pack_fragmented(v.begin(), v.end(), v.size()); } template utils::chunked_vector partially_deserialize_listlike(View in) { auto nr = read_collection_size(in); utils::chunked_vector elements; elements.reserve(nr); for (int i = 0; i != nr; ++i) { elements.emplace_back(read_collection_value(in)); } return elements; } template utils::chunked_vector partially_deserialize_listlike(managed_bytes_view in); template utils::chunked_vector partially_deserialize_listlike(fragmented_temporary_buffer::view in); template std::vector> partially_deserialize_map(View in) { auto nr = read_collection_size(in); std::vector> elements; elements.reserve(nr); for (int i = 0; i != nr; ++i) { auto key = managed_bytes(read_collection_key(in)); auto value = managed_bytes_opt(read_collection_value_nonnull(in)); if (!value) { on_internal_error(tlogger, "NULL value in map"); } elements.emplace_back(std::move(key), std::move(*value)); } return elements; } template std::vector> partially_deserialize_map(managed_bytes_view in); template std::vector> partially_deserialize_map(fragmented_temporary_buffer::view in); list_type list_type_impl::get_instance(data_type elements, bool is_multi_cell) { return intern::get_instance(elements, is_multi_cell); } sstring make_list_type_name(data_type elements, bool is_multi_cell) { sstring ret = ""; if (!is_multi_cell) { ret = "org.apache.cassandra.db.marshal.FrozenType("; } ret += "org.apache.cassandra.db.marshal.ListType(" + elements->name() + ")"; if (!is_multi_cell) { ret += ")"; } return ret; } list_type_impl::list_type_impl(data_type elements, bool is_multi_cell) : concrete_type(kind::list, make_list_type_name(elements, is_multi_cell), elements, is_multi_cell) { _contains_set_or_map = _elements->contains_set_or_map(); } 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 { SCYLLA_ASSERT(!_is_multi_cell); auto* p = dynamic_cast(&previous); if (!p) { return false; } return _elements->is_compatible_with(*p->_elements); } static bool is_value_compatible_with_internal(const abstract_type& t, const abstract_type& other); bool list_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const { auto& lp = dynamic_cast(previous); return is_value_compatible_with_internal(*_elements, *lp._elements); } template static void validate_aux(const list_type_impl& t, View v) { auto nr = read_collection_size(v); for (int i = 0; i != nr; ++i) { auto val_opt = read_collection_value(v); if (val_opt) { t.get_elements_type()->validate(*val_opt); } } if (v.size_bytes()) { auto hex = with_linearized(v, [] (bytes_view bv) { return to_hex(bv); }); throw marshal_exception(format("Validation failed for type {}: bytes remaining after " "reading all {} elements of the list -> [{}]", t.name(), nr, hex)); } } static void serialize_list(const list_type_impl& t, const void* value, bytes::iterator& out) { auto& s = t.from_value(value); write_collection_size(out, s.size()); for (auto&& e : s) { write_collection_value(out, t.get_elements_type(), e); } } template data_value list_type_impl::deserialize(View in) const { auto nr = read_collection_size(in); native_type s; s.reserve(nr); for (int i = 0; i != nr; ++i) { auto serialized_value_opt = read_collection_value(in); if (serialized_value_opt) { auto e = _elements->deserialize(*serialized_value_opt); s.push_back(std::move(e)); } else { s.push_back(data_value::make_null(data_type(shared_from_this()))); } } return make_value(std::move(s)); } template data_value list_type_impl::deserialize<>(ser::buffer_view) const; static sstring vector_to_string(const std::vector& v, std::string_view sep) { return fmt::to_string(fmt::join( v | std::views::transform([] (const data_value& e) { return e.type()->to_string_impl(e); }), sep)); } template static std::optional update_listlike( const listlike_collection_type_impl& c, F&& f, shared_ptr updated) { if (auto e = c.get_elements_type()->update_user_type(updated)) { return std::make_optional(f(std::move(*e), c.is_multi_cell())); } return std::nullopt; } tuple_type_impl::tuple_type_impl(kind k, sstring name, std::vector types, bool freeze_inner) : concrete_type(k, std::move(name), { }), _types(std::move(types)) { if (freeze_inner) { for (auto& t : _types) { t = t->freeze(); } } set_contains_collections(); } tuple_type_impl::tuple_type_impl(std::vector types, bool freeze_inner) : tuple_type_impl{kind::tuple, make_name(types), std::move(types), freeze_inner} { set_contains_collections(); } tuple_type_impl::tuple_type_impl(std::vector types) : tuple_type_impl(std::move(types), true) { set_contains_collections(); } void tuple_type_impl::set_contains_collections() { for (const data_type& t : _types) { if (t->contains_set_or_map()) { _contains_set_or_map = true; break; } } if (_contains_set_or_map) { _contains_collection = true; return; } for (const data_type& t : _types) { if (t->contains_collection()) { _contains_collection = true; break; } } } shared_ptr tuple_type_impl::get_instance(std::vector types) { return intern::get_instance(std::move(types)); } template static void validate_aux(const tuple_type_impl& t, View v) { auto ti = t.all_types().begin(); while (ti != t.all_types().end() && v.size_bytes()) { std::optional e = read_tuple_element(v); if (e) { (*ti)->validate(*e); } ++ti; } size_t extra_elements = 0; while (!v.empty()) { read_tuple_element(v); extra_elements += 1; } if (extra_elements > 0) { // This function is called for both tuple and user_type, print the name too throw marshal_exception(format("Value of type {} contained too many fields (expected {}, got {})", t.name(), t.size(), t.size() + extra_elements)); } } sstring vector_type_impl::make_name(data_type type, vector_dimension_t dimension) { // To keep format compatibility with Origin we never wrap // vector name into // "org.apache.cassandra.db.marshal.FrozenType(...)". return seastar::format("org.apache.cassandra.db.marshal.VectorType({}, {})", type->name(), dimension); } vector_type_impl::vector_type_impl(data_type elements, vector_dimension_t dimension) : concrete_type(kind::vector, make_name(elements, dimension), elements->value_length_if_fixed() ? std::optional(elements->value_length_if_fixed().value() * dimension) : std::nullopt), _elements_type(elements), _dimension(dimension) { _contains_set_or_map = _elements_type->contains_set_or_map(); } shared_ptr vector_type_impl::get_instance(data_type elements, vector_dimension_t dimension) { return intern::get_instance(elements, dimension); } static void serialize_vector(const vector_type_impl& type, const vector_type_impl::native_type* val, bytes::iterator& out) { auto elements_type = type.get_elements_type(); if (type.value_length_if_fixed()) { for (const auto& value : *val) { value.serialize(out); } } else { for (const auto& value : *val) { size_t val_len = value.serialized_size(); out += unsigned_vint::serialize(val_len, out); value.serialize(out); } } } std::strong_ordering vector_type_impl::compare_vectors(data_type elements, vector_dimension_t dimension, managed_bytes_view o1, managed_bytes_view o2) { if (o1.empty()) { return o2.empty() ? std::strong_ordering::equal : std::strong_ordering::less; } else if (o2.empty()) { return std::strong_ordering::greater; } for (size_t i = 0; i < dimension; i++) { auto v1 = read_vector_element(o1, elements->value_length_if_fixed()); auto v2 = read_vector_element(o2, elements->value_length_if_fixed()); auto cmp = elements->compare(v1, v2); if (cmp != 0) { return cmp; } } return std::strong_ordering::equal; } namespace { // Visitor that deserializes all elements of a vector in a single visit() call. // This avoids per-element virtual dispatch through visit() which is the // bottleneck for high-dimensional vectors. template struct deserialize_vector_visitor { const vector_type_impl& t; View& v; data_value operator()(const counter_type_impl&) { return deserialize_loop([&] (View elem) { return static_cast(*long_type).make_value( read_simple_exactly(elem)); }); } data_value operator()(const bytes_type_impl& element_type) { return deserialize_loop([&] (View elem) { return element_type.make_value( std::make_unique(linearized(elem))); }); } data_value operator()(const empty_type_impl&) { return deserialize_loop([] (View) { return data_value(empty_type_representation()); }); } data_value operator()(const ascii_type_impl& element_type) { return deserialize_string_simple(element_type); } data_value operator()(const utf8_type_impl& element_type) { return deserialize_string_simple(element_type); } // Complex element types: fall back to per-element virtual dispatch. // These are rare in practice (vectors of tuples/maps/etc are uncommon). data_value operator()(const reversed_type_impl&) { return deserialize_generic(); } data_value operator()(const list_type_impl&) { return deserialize_generic(); } data_value operator()(const map_type_impl&) { return deserialize_generic(); } data_value operator()(const set_type_impl&) { return deserialize_generic(); } data_value operator()(const tuple_type_impl&) { return deserialize_generic(); } data_value operator()(const user_type_impl&) { return deserialize_generic(); } data_value operator()(const vector_type_impl&) { return deserialize_generic(); } // Simple fixed-size types: deserialize_value is fully inlined by the // compiler, no virtual dispatch per element. // Each type is listed explicitly so that adding a new type forces // a conscious decision about how to handle it here. data_value operator()(const boolean_type_impl& t) { return deserialize_simple(t); } data_value operator()(const byte_type_impl& t) { return deserialize_simple(t); } data_value operator()(const date_type_impl& t) { return deserialize_simple(t); } data_value operator()(const decimal_type_impl& t) { return deserialize_simple(t); } data_value operator()(const double_type_impl& t) { return deserialize_simple(t); } data_value operator()(const duration_type_impl& t) { return deserialize_simple(t); } data_value operator()(const float_type_impl& t) { return deserialize_simple(t); } data_value operator()(const inet_addr_type_impl& t) { return deserialize_simple(t); } data_value operator()(const int32_type_impl& t) { return deserialize_simple(t); } data_value operator()(const long_type_impl& t) { return deserialize_simple(t); } data_value operator()(const short_type_impl& t) { return deserialize_simple(t); } data_value operator()(const simple_date_type_impl& t) { return deserialize_simple(t); } data_value operator()(const time_type_impl& t) { return deserialize_simple(t); } data_value operator()(const timestamp_type_impl& t) { return deserialize_simple(t); } data_value operator()(const timeuuid_type_impl& t) { return deserialize_simple(t); } data_value operator()(const uuid_type_impl& t) { return deserialize_simple(t); } data_value operator()(const varint_type_impl& t) { return deserialize_simple(t); } private: template data_value deserialize_simple(const T& element_type) { return deserialize_loop([&] (View elem) -> data_value { if (!elem.size_bytes()) { return element_type.make_empty(); } return element_type.make_value(deserialize_value(element_type, elem)); }); } // String types (ascii, utf8): sstring has no constructor from empty_t, // so we skip the make_empty() check. Element size 0 is already rejected // by read_vector_element anyway. template data_value deserialize_string_simple(const T& element_type) { return deserialize_loop([&] (View elem) -> data_value { return element_type.make_value(deserialize_value(element_type, elem)); }); } data_value deserialize_generic() { return deserialize_loop([&] (View elem) { return t.get_elements_type()->deserialize(elem); }); } template data_value deserialize_loop(Func&& func) { auto value_length = t.get_elements_type()->value_length_if_fixed(); vector_type_impl::native_type ret; ret.reserve(t.get_dimension()); if (value_length) { for (size_t i = 0; i < t.get_dimension(); i++) { ret.push_back(func(read_vector_element_fixed(v, *value_length))); } } else { for (size_t i = 0; i < t.get_dimension(); i++) { ret.push_back(func(read_vector_element_variable(v))); } } return data_value::make(t.shared_from_this(), std::make_unique(std::move(ret))); } }; } // anonymous namespace template data_value deserialize_vector(const vector_type_impl& t, View v){ return visit(*t.get_elements_type(), deserialize_vector_visitor{t, v}); } static size_t vector_serialized_size(const vector_type_impl::native_type* v) { if (v->empty()) { return 0; } auto type = v->front().type(); size_t len = 0; if (type->value_length_if_fixed()) { len = v->size() * type->value_length_if_fixed().value(); } else { for (const auto& value : *v) { size_t val_len = value.serialized_size(); len += (size_t)unsigned_vint::serialized_size(val_len) + val_len; } } return len; } template static void validate_aux(const vector_type_impl& t, View v) { for (size_t i = 0; i < t.get_dimension(); ++i) { auto val = read_vector_element(v, t.get_elements_type()->value_length_if_fixed()); t.get_elements_type()->validate(val); } if (v.size_bytes()) { auto hex = with_linearized(v, [] (bytes_view bv) { return to_hex(bv); }); throw marshal_exception(format("Validation failed for type {}: bytes remaining after " "reading all {} elements of the vector -> [{}]", t.name(), t.get_dimension(), hex)); } } static std::optional update_user_type_aux( const vector_type_impl& m, const shared_ptr updated) { auto old_elements = m.get_elements_type(); if (auto new_elements = old_elements->update_user_type(updated)) { return std::make_optional(static_pointer_cast( vector_type_impl::get_instance(*new_elements, m.get_dimension()))); } return std::nullopt; } namespace { template struct validate_visitor { const View& v; ; void operator()(const reversed_type_impl& t) { visit(*t.underlying_type(), validate_visitor{v}); } void operator()(const counter_type_impl&) { visit(*long_type, validate_visitor{v}); } void operator()(const abstract_type&) {} template void operator()(const integer_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != sizeof(T)) { throw marshal_exception(format("Validation failed for type {}: got {:d} bytes", t.name(), v.size_bytes())); } } void operator()(const byte_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 1) { throw marshal_exception(format("Expected 1 byte for a tinyint ({:d})", v.size_bytes())); } } void operator()(const short_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 2) { throw marshal_exception(format("Expected 2 bytes for a smallint ({:d})", v.size_bytes())); } } void operator()(const ascii_type_impl&) { // ASCII can be validated independently for each fragment for (bytes_view frag : fragment_range(v)) { if (!utils::ascii::validate(frag)) { throw marshal_exception("Validation failed - non-ASCII character in an ASCII string"); } } } void operator()(const utf8_type_impl&) { auto error_pos = with_simplified(v, [] (FragmentedView auto v) { return utils::utf8::validate_with_error_position_fragmented(v); }); if (error_pos) { throw marshal_exception(format("Validation failed - non-UTF8 character in a UTF8 string, at byte offset {}", *error_pos)); } } void operator()(const bytes_type_impl& t) {} void operator()(const boolean_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 1) { throw marshal_exception(format("Validation failed for boolean, got {:d} bytes", v.size_bytes())); } } void operator()(const timeuuid_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 16) { throw marshal_exception(format("Validation failed for timeuuid - got {:d} bytes", v.size_bytes())); } View in = v; auto msb = read_simple(in); auto lsb = read_simple(in); utils::UUID uuid(msb, lsb); if (uuid.version() != 1) { throw marshal_exception(format("Unsupported UUID version ({:d})", uuid.version())); } } void operator()(const timestamp_date_base_class& t) { if (v.empty()) { return; } if (v.size_bytes() != sizeof(uint64_t)) { throw marshal_exception(format("Validation failed for timestamp - got {:d} bytes", v.size_bytes())); } } void operator()(const duration_type_impl& t) { if (v.size_bytes() < 3) { throw marshal_exception(format("Expected at least 3 bytes for a duration, got {:d}", v.size_bytes())); } common_counter_type months, days, nanoseconds; std::tie(months, days, nanoseconds) = with_linearized(v, [] (bytes_view bv) { return deserialize_counters(bv); }); auto check_counter_range = [] (common_counter_type value, auto counter_value_type_instance, std::string_view counter_name) { using counter_value_type = decltype(counter_value_type_instance); if (static_cast(value) != value) { throw marshal_exception(seastar::format("The duration {} ({:d}) must be a {:d} bit integer", counter_name, value, std::numeric_limits::digits + 1)); } }; check_counter_range(months, months_counter::value_type(), "months"); check_counter_range(days, days_counter::value_type(), "days"); check_counter_range(nanoseconds, nanoseconds_counter::value_type(), "nanoseconds"); if (!(((months <= 0) && (days <= 0) && (nanoseconds <= 0)) || ((months >= 0) && (days >= 0) && (nanoseconds >= 0)))) { throw marshal_exception(format("The duration months, days, and nanoseconds must be all of " "the same sign ({:d}, {:d}, {:d})", months, days, nanoseconds)); } } template void operator()(const floating_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != sizeof(T)) { throw marshal_exception(format("Expected {:d} bytes for a floating type, got {:d}", sizeof(T), v.size_bytes())); } } void operator()(const simple_date_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 4) { throw marshal_exception(format("Expected 4 byte long for date ({:d})", v.size_bytes())); } } void operator()(const time_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 8) { throw marshal_exception(format("Expected 8 byte long for time ({:d})", v.size_bytes())); } } void operator()(const uuid_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != 16) { throw marshal_exception(format("Validation failed for uuid - got {:d} bytes", v.size_bytes())); } } void operator()(const inet_addr_type_impl& t) { if (v.empty()) { return; } if (v.size_bytes() != sizeof(uint32_t) && v.size_bytes() != 16) { throw marshal_exception(format("Validation failed for inet_addr - got {:d} bytes", v.size_bytes())); } } void operator()(const map_type_impl& t) { with_simplified(v, [&] (FragmentedView auto v) { validate_aux(t, v); }); } void operator()(const set_type_impl& t) { with_simplified(v, [&] (FragmentedView auto v) { validate_aux(t, v); }); } void operator()(const list_type_impl& t) { with_simplified(v, [&] (FragmentedView auto v) { validate_aux(t, v); }); } void operator()(const tuple_type_impl& t) { with_simplified(v, [&] (FragmentedView auto v) { validate_aux(t, v); }); } void operator()(const vector_type_impl& t) { with_simplified(v, [&] (FragmentedView auto v) { validate_aux(t, v); }); } }; } template void abstract_type::validate(const View& view) const { visit(*this, validate_visitor{view}); } // Explicit instantiation. template void abstract_type::validate<>(const single_fragmented_view&) const; template void abstract_type::validate<>(const fragmented_temporary_buffer::view&) const; template void abstract_type::validate<>(const managed_bytes_view&) const; void abstract_type::validate(bytes_view v) const { visit(*this, validate_visitor{single_fragmented_view(v)}); } static void serialize_aux(const tuple_type_impl& type, const tuple_type_impl::native_type* val, bytes::iterator& out) { SCYLLA_ASSERT(val); auto& elems = *val; SCYLLA_ASSERT(elems.size() <= type.size()); for (size_t i = 0; i < elems.size(); ++i) { const abstract_type& t = type.type(i)->without_reversed(); const data_value& v = elems[i]; if (!v.is_null() && t != *v.type()) { throw std::runtime_error(format("tuple element type mismatch: expected {}, got {}", t.name(), v.type()->name())); } if (v.is_null()) { write(out, int32_t(-1)); } else { write(out, int32_t(v.serialized_size())); v.serialize(out); } } } static size_t concrete_serialized_size(const utils::multiprecision_int& num); static void serialize_varint_aux(bytes::iterator& out, const boost::multiprecision::cpp_int& num, uint8_t mask) { struct inserter_with_prefix { bytes::iterator& out; uint8_t mask; bool first = true; inserter_with_prefix& operator*() { return *this; } inserter_with_prefix& operator=(uint8_t value) { if (first) { if (value & 0x80) { *out++ = 0 ^ mask; } first = false; } *out = value ^ mask; return *this; } inserter_with_prefix& operator++() { ++out; return *this; } }; export_bits(num, inserter_with_prefix{out, mask}, 8); } static void serialize_varint(bytes::iterator& out, const boost::multiprecision::cpp_int& num) { if (num < 0) { serialize_varint_aux(out, -num - 1, 0xff); } else { serialize_varint_aux(out, num, 0); } } static void serialize_varint(bytes::iterator& out, const utils::multiprecision_int& num) { serialize_varint(out, static_cast(num)); } static void serialize(const abstract_type& t, const void* value, bytes::iterator& out); namespace { struct serialize_visitor { bytes::iterator& out; ; void operator()(const reversed_type_impl& t, const void* v) { return serialize(*t.underlying_type(), v, out); } void operator()(const counter_type_impl& t, const void* v) { return serialize(*long_type, v, out); } template void operator()(const integer_type_impl& t, const typename integer_type_impl::native_type* v1) { if (v1->empty()) { return; } auto v = v1->get(); auto u = net::hton(v); out = std::copy_n(reinterpret_cast(&u), sizeof(u), out); } void operator()(const string_type_impl& t, const string_type_impl::native_type* v) { out = std::copy(v->begin(), v->end(), out); } void operator()(const bytes_type_impl& t, const bytes* v) { out = std::copy(v->begin(), v->end(), out); } void operator()(const boolean_type_impl& t, const boolean_type_impl::native_type* v) { if (!v->empty()) { *out++ = char(*v); } } void operator()(const timestamp_date_base_class& t, const timestamp_date_base_class::native_type* v1) { if (v1->empty()) { return; } uint64_t v = v1->get().time_since_epoch().count(); v = net::hton(v); out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); } void operator()(const timeuuid_type_impl& t, const timeuuid_type_impl::native_type* uuid1) { if (uuid1->empty()) { return; } auto uuid = uuid1->get(); uuid.serialize(out); } void operator()(const simple_date_type_impl& t, const simple_date_type_impl::native_type* v1) { if (v1->empty()) { return; } uint32_t v = v1->get(); v = net::hton(v); out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); } void operator()(const time_type_impl& t, const time_type_impl::native_type* v1) { if (v1->empty()) { return; } uint64_t v = v1->get(); v = net::hton(v); out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); } void operator()(const empty_type_impl& t, const void*) {} void operator()(const uuid_type_impl& t, const uuid_type_impl::native_type* value) { if (value->empty()) { return; } value->get().serialize(out); } void operator()(const inet_addr_type_impl& t, const inet_addr_type_impl::native_type* ipv) { if (ipv->empty()) { return; } auto& ip = ipv->get(); switch (ip.in_family()) { case inet_address::family::INET: { const ::in_addr& in = ip; out = std::copy_n(reinterpret_cast(&in.s_addr), sizeof(in.s_addr), out); break; } case inet_address::family::INET6: { const ::in6_addr& i6 = ip; out = std::copy_n(i6.s6_addr, ip.size(), out); break; } } } template void operator()(const floating_type_impl& t, const typename floating_type_impl::native_type* value) { if (value->empty()) { return; } T d = *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(); } typename int_of_size::itype i; memcpy(&i, &d, sizeof(T)); auto u = net::hton(i); out = std::copy_n(reinterpret_cast(&u), sizeof(u), out); } void operator()(const varint_type_impl& t, const varint_type_impl::native_type* num1) { if (num1->empty()) { return; } serialize_varint(out, num1->get()); } void operator()(const decimal_type_impl& t, const decimal_type_impl::native_type* bd1) { if (bd1->empty()) { return; } auto&& bd = std::move(*bd1).get(); auto u = net::hton(bd.scale()); out = std::copy_n(reinterpret_cast(&u), sizeof(int32_t), out); serialize_varint(out, bd.unscaled_value()); } void operator()(const duration_type_impl& t, const duration_type_impl::native_type* m) { if (m->empty()) { return; } const auto& d = m->get(); out += signed_vint::serialize(d.months, out); out += signed_vint::serialize(d.days, out); out += signed_vint::serialize(d.nanoseconds, out); } void operator()(const list_type_impl& t, const void* value) { serialize_list(t, value, out); } void operator()(const map_type_impl& t, const void* value) { serialize_map(t, value, out); } void operator()(const set_type_impl& t, const void* value) { serialize_set(t, value, out); } void operator()(const tuple_type_impl& t, const tuple_type_impl::native_type* value) { return serialize_aux(t, value, out); } void operator()(const vector_type_impl& t, const vector_type_impl::native_type* value) { serialize_vector(t, value, out); } }; } static void serialize(const abstract_type& t, const void* value, bytes::iterator& out) { visit(t, value, serialize_visitor{out}); } template data_value collection_type_impl::deserialize_impl(View v) const { struct visitor { View v; ; data_value operator()(const abstract_type&) { on_internal_error(tlogger, "collection_type_impl::deserialize called on a non-collection type. This should be impossible."); } data_value operator()(const list_type_impl& t) { return t.deserialize(v); } data_value operator()(const map_type_impl& t) { return t.deserialize(v); } data_value operator()(const set_type_impl& t) { return t.deserialize(v); } }; return ::visit(*this, visitor{v}); } // Explicit instantiation. // This should be repeated for every View type passed to collection_type_impl::deserialize. template data_value collection_type_impl::deserialize_impl<>(ser::buffer_view) const; template data_value collection_type_impl::deserialize_impl<>(fragmented_temporary_buffer::view) const; template data_value collection_type_impl::deserialize_impl<>(single_fragmented_view) const; template data_value collection_type_impl::deserialize_impl<>(managed_bytes_view) const; template int read_collection_size(ser::buffer_view& in); template ser::buffer_view read_collection_value_nonnull(ser::buffer_view& in); template data_value deserialize_aux(const tuple_type_impl& t, View v) { tuple_type_impl::native_type ret; ret.reserve(t.all_types().size()); auto ti = t.all_types().begin(); while (ti != t.all_types().end() && v.size_bytes()) { data_value obj = data_value::make_null(*ti); std::optional e = read_tuple_element(v); if (e) { obj = (*ti)->deserialize(*e); } ret.push_back(std::move(obj)); ++ti; } while (ti != t.all_types().end()) { ret.push_back(data_value::make_null(*ti++)); } return data_value::make(t.shared_from_this(), std::make_unique(std::move(ret))); } template std::optional> read_nth_tuple_element(View serialized_tuple, std::size_t element_index) { for (std::size_t i = 0; serialized_tuple.size_bytes() > 0; i++) { // element being std::nullopt means that its value is NULL. std::optional element = read_tuple_element(serialized_tuple); if (i == element_index) { return std::make_optional(element); } } return std::nullopt; } template std::optional> read_nth_tuple_element(managed_bytes_view serialized_tuple, std::size_t element_index); template std::optional> read_nth_tuple_element( fragmented_temporary_buffer::view serialized_tuple, std::size_t element_index); template std::optional read_nth_user_type_field(View serialized_user_type, std::size_t element_index) { // UDT is serialized as a tuple of field values, so we can use read_nth_tuple_element to read // the value of nth UDT field. std::optional> read_field = read_nth_tuple_element(serialized_user_type, element_index); if (!read_field.has_value()) { // There is no field with this index, assume that this field is NULL. return std::nullopt; } // Field value found, return it. return *read_field; } template std::optional read_nth_user_type_field(managed_bytes_view serialized_user_type, std::size_t element_index); template std::optional read_nth_user_type_field( fragmented_temporary_buffer::view serialized_user_type, std::size_t element_index); template utils::multiprecision_int deserialize_value(const varint_type_impl&, View v) { if (v.empty()) { throw marshal_exception("cannot deserialize multiprecision int - empty buffer"); } skip_empty_fragments(v); bool negative = v.current_fragment().front() < 0; utils::multiprecision_int num; while (v.size_bytes()) { for (uint8_t b : v.current_fragment()) { if (negative) { b = ~b; } num <<= 8; num += b; } v.remove_current(); } if (negative) { num += 1; } return negative ? -num : num; } template T deserialize_value(const floating_type_impl&, View v) { typename int_of_size::itype i = read_simple::itype>(v); if (v.size_bytes()) { throw marshal_exception(format("cannot deserialize floating - {:d} bytes left", v.size_bytes())); } T d; memcpy(&d, &i, sizeof(T)); return d; } template big_decimal deserialize_value(const decimal_type_impl&, View v) { auto scale = read_simple(v); auto unscaled = deserialize_value(static_cast(*varint_type), v); return big_decimal(scale, unscaled); } template cql_duration deserialize_value(const duration_type_impl& t, View v) { common_counter_type months, days, nanoseconds; std::tie(months, days, nanoseconds) = with_linearized(v, [] (bytes_view bv) { return deserialize_counters(bv); }); return cql_duration(months_counter(months), days_counter(days), nanoseconds_counter(nanoseconds)); } template inet_address deserialize_value(const inet_addr_type_impl&, View v) { switch (v.size_bytes()) { case 4: // gah. read_simple_be, please... return inet_address(::in_addr{net::hton(read_simple(v))}); case 16:; ::in6_addr buf; read_fragmented(v, sizeof(buf), reinterpret_cast(&buf)); return inet_address(buf); default: throw marshal_exception(format("cannot deserialize inet_address, unsupported size {:d} bytes", v.size_bytes())); } } template utils::UUID deserialize_value(const uuid_type_impl&, View v) { auto msb = read_simple(v); auto lsb = read_simple(v); if (v.size_bytes()) { throw marshal_exception(format("cannot deserialize uuid, {:d} bytes left", v.size_bytes())); } return utils::UUID(msb, lsb); } template utils::UUID deserialize_value(const timeuuid_type_impl&, View v) { return deserialize_value(static_cast(*uuid_type), v); } template db_clock::time_point deserialize_value(const timestamp_date_base_class&, View v) { auto v2 = read_simple_exactly(v); return db_clock::time_point(db_clock::duration(v2)); } template uint32_t deserialize_value(const simple_date_type_impl&, View v) { return read_simple_exactly(v); } template int64_t deserialize_value(const time_type_impl&, View v) { return read_simple_exactly(v); } template bool deserialize_value(const boolean_type_impl&, View v) { if (v.size_bytes() != 1) { throw marshal_exception(format("cannot deserialize boolean, size mismatch ({:d})", v.size_bytes())); } skip_empty_fragments(v); return v.current_fragment().front() != 0; } template T deserialize_value(const integer_type_impl& t, View v) { return read_simple_exactly(v); } template sstring deserialize_value(const string_type_impl&, View v) { // FIXME: validation? sstring buf(sstring::initialized_later(), v.size_bytes()); auto out = buf.begin(); while (v.size_bytes()) { out = std::copy(v.current_fragment().begin(), v.current_fragment().end(), out); v.remove_current(); } return buf; } template decltype(auto) deserialize_value(const T& t, bytes_view v) { return deserialize_value(t, single_fragmented_view(v)); } namespace { template struct deserialize_visitor { View v; data_value operator()(const reversed_type_impl& t) { return t.underlying_type()->deserialize(v); } template data_value operator()(const T& t) { if (!v.size_bytes()) { return t.make_empty(); } return t.make_value(deserialize_value(t, v)); } data_value operator()(const ascii_type_impl& t) { return t.make_value(deserialize_value(t, v)); } data_value operator()(const utf8_type_impl& t) { return t.make_value(deserialize_value(t, v)); } data_value operator()(const bytes_type_impl& t) { return t.make_value(std::make_unique(linearized(v))); } data_value operator()(const counter_type_impl& t) { return static_cast(*long_type).make_value(read_simple_exactly(v)); } data_value operator()(const list_type_impl& t) { return t.deserialize(v); } data_value operator()(const map_type_impl& t) { return t.deserialize(v); } data_value operator()(const set_type_impl& t) { return t.deserialize(v); } data_value operator()(const tuple_type_impl& t) { return deserialize_aux(t, v); } data_value operator()(const vector_type_impl& t) { return deserialize_vector(t,v); } data_value operator()(const user_type_impl& t) { return deserialize_aux(t, v); } data_value operator()(const empty_type_impl& t) { return data_value(empty_type_representation()); } }; } template data_value abstract_type::deserialize_impl(View v) const { return visit(*this, deserialize_visitor{v}); } // Explicit instantiation. // This should be repeated for every type passed to deserialize(). template data_value abstract_type::deserialize_impl<>(fragmented_temporary_buffer::view) const; template data_value abstract_type::deserialize_impl<>(single_fragmented_view) const; template data_value abstract_type::deserialize_impl<>(ser::buffer_view) const; template data_value abstract_type::deserialize_impl<>(managed_bytes_view) const; std::strong_ordering compare_aux(const tuple_type_impl& t, const managed_bytes_view& v1, const managed_bytes_view& v2) { // This is a slight modification of lexicographical_tri_compare: // when the only difference between the tuples is that one of them has additional trailing nulls, // we consider them equal. For example, in the following CQL scenario: // 1. create type ut (a int); // 2. create table cf (a int primary key, b frozen); // 3. insert into cf (a, b) values (0, (0)); // 4. alter type ut add b int; // 5. select * from cf where b = {a:0,b:null}; // the row with a = 0 should be returned, even though the value stored in the database is shorter // (by one null) than the value given by the user. auto types_first = t.all_types().begin(); auto types_last = t.all_types().end(); auto first1 = tuple_deserializing_iterator::start(v1); auto last1 = tuple_deserializing_iterator::finish(v1); auto first2 = tuple_deserializing_iterator::start(v2); auto last2 = tuple_deserializing_iterator::finish(v2); while (types_first != types_last && first1 != last1 && first2 != last2) { if (auto c = tri_compare_opt(*types_first, *first1, *first2); c != 0) { return c; } ++first1; ++first2; ++types_first; } while (types_first != types_last && first1 != last1) { if (*first1) { return std::strong_ordering::greater; } ++first1; ++types_first; } while (types_first != types_last && first2 != last2) { if (*first2) { return std::strong_ordering::less; } ++first2; ++types_first; } return std::strong_ordering::equal; } namespace { struct compare_visitor { managed_bytes_view v1; managed_bytes_view v2; template Func> requires std::same_as> std::strong_ordering with_empty_checks(Func func) { if (v1.empty()) { return v2.empty() ? std::strong_ordering::equal : std::strong_ordering::less; } if (v2.empty()) { return std::strong_ordering::greater; } return func(); } template std::strong_ordering operator()(const simple_type_impl&) { return with_empty_checks([&] { T a = simple_type_traits::read_nonempty(v1); T b = simple_type_traits::read_nonempty(v2); return a <=> b; }); } std::strong_ordering operator()(const string_type_impl&) { return compare_unsigned(v1, v2); } std::strong_ordering operator()(const bytes_type_impl&) { return compare_unsigned(v1, v2); } std::strong_ordering operator()(const duration_type_impl&) { return compare_unsigned(v1, v2); } std::strong_ordering operator()(const inet_addr_type_impl&) { return compare_unsigned(v1, v2); } std::strong_ordering operator()(const date_type_impl&) { // This is not the same behaviour as timestamp_type_impl return compare_unsigned(v1, v2); } std::strong_ordering operator()(const timeuuid_type_impl&) { return with_empty_checks([&] { return with_linearized(v1, [&] (bytes_view v1) { return with_linearized(v2, [&] (bytes_view v2) { return utils::timeuuid_tri_compare(v1, v2); }); }); }); } std::strong_ordering operator()(const listlike_collection_type_impl& l) { using llpdi = listlike_partial_deserializing_iterator; return with_empty_checks([&] { return std::lexicographical_compare_three_way(llpdi::begin(v1), llpdi::end(v1), llpdi::begin(v2), llpdi::end(v2), [&] (const managed_bytes_view_opt& o1, const managed_bytes_view_opt& o2) { if (!o1.has_value() || !o2.has_value()) { return o1.has_value() <=> o2.has_value(); } else { return l.get_elements_type()->compare(*o1, *o2); } }); }); } std::strong_ordering operator()(const map_type_impl& m) { return map_type_impl::compare_maps(m.get_keys_type(), m.get_values_type(), v1, v2); } std::strong_ordering operator()(const uuid_type_impl&) { if (v1.size() < 16) { return v2.size() < 16 ? std::strong_ordering::equal : std::strong_ordering::less; } if (v2.size() < 16) { return std::strong_ordering::greater; } auto c1 = (v1[6] >> 4) & 0x0f; auto c2 = (v2[6] >> 4) & 0x0f; if (c1 != c2) { return c1 <=> c2; } if (c1 == 1) { return with_linearized(v1, [&] (bytes_view v1) { return with_linearized(v2, [&] (bytes_view v2) { return utils::uuid_tri_compare_timeuuid(v1, v2); }); }); } return compare_unsigned(v1, v2); } std::strong_ordering operator()(const empty_type_impl&) { return std::strong_ordering::equal; } std::strong_ordering operator()(const tuple_type_impl& t) { return compare_aux(t, v1, v2); } std::strong_ordering operator()(const vector_type_impl& t) { return vector_type_impl::compare_vectors(t.get_elements_type(),t.get_dimension(), v1, v2); } std::strong_ordering operator()(const counter_type_impl&) { // untouched (empty) counter evaluates as 0 const auto a = v1.empty() ? 0 : simple_type_traits::read_nonempty(v1); const auto b = v2.empty() ? 0 : simple_type_traits::read_nonempty(v2); return a <=> b; } std::strong_ordering operator()(const decimal_type_impl& d) { return with_empty_checks([&] { auto a = deserialize_value(d, v1); auto b = deserialize_value(d, v2); return a <=> b; }); } std::strong_ordering operator()(const varint_type_impl& v) { return with_empty_checks([&] { auto a = deserialize_value(v, v1); auto b = deserialize_value(v, v2); return a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater; }); } template std::strong_ordering operator()(const floating_type_impl&) { return with_empty_checks([&] { 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 std::strong_ordering::equal; } else if (std::isnan(a)) { return std::strong_ordering::greater; } else if (std::isnan(b)) { return std::strong_ordering::less; } // also -0 < 0 if (std::signbit(a) && !std::signbit(b)) { return std::strong_ordering::less; } else if (!std::signbit(a) && std::signbit(b)) { return std::strong_ordering::greater; } // note: float <=> returns std::partial_ordering return a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater; }); } std::strong_ordering operator()(const reversed_type_impl& r) { return r.underlying_type()->compare(v2, v1); } }; } std::strong_ordering abstract_type::compare(bytes_view v1, bytes_view v2) const { return compare(managed_bytes_view(v1), managed_bytes_view(v2)); } std::strong_ordering abstract_type::compare(managed_bytes_view v1, managed_bytes_view v2) const { try { return visit(*this, compare_visitor{v1, v2}); } catch (const marshal_exception&) { on_types_internal_error(std::current_exception()); } } std::strong_ordering abstract_type::compare(managed_bytes_view v1, bytes_view v2) const { return compare(v1, managed_bytes_view(v2)); } std::strong_ordering abstract_type::compare(bytes_view v1, managed_bytes_view v2) const { return compare(managed_bytes_view(v1), v2); } bool abstract_type::equal(bytes_view v1, bytes_view v2) const { return ::visit(*this, [&](const auto& t) { if (is_byte_order_equal_visitor{}(t)) { return compare_unsigned(v1, v2) == 0; } return compare_visitor{v1, v2}(t) == 0; }); } bool abstract_type::equal(managed_bytes_view v1, managed_bytes_view v2) const { return ::visit(*this, [&](const auto& t) { if (is_byte_order_equal_visitor{}(t)) { return compare_unsigned(v1, v2) == 0; } return compare_visitor{v1, v2}(t) == 0; }); } bool abstract_type::equal(managed_bytes_view v1, bytes_view v2) const { return equal(v1, managed_bytes_view(v2)); } bool abstract_type::equal(bytes_view v1, managed_bytes_view v2) const { return equal(managed_bytes_view(v1), v2); } // Count number of ':' which are not preceded by '\'. static std::size_t count_segments(std::string_view v) { std::size_t segment_count = 1; char prev_ch = '.'; for (char ch : v) { if (ch == ':' && prev_ch != '\\') { ++segment_count; } prev_ch = ch; } return segment_count; } // Split on ':', unless it's preceded by '\'. static std::vector split_field_strings(std::string_view v) { if (v.empty()) { return std::vector(); } std::vector result; result.reserve(count_segments(v)); std::size_t prev = 0; char prev_ch = '.'; for (std::size_t i = 0; i < v.size(); ++i) { if (v[i] == ':' && prev_ch != '\\') { result.push_back(v.substr(prev, i - prev)); prev = i + 1; } prev_ch = v[i]; } result.push_back(v.substr(prev, v.size() - prev)); return result; } // Replace "\:" with ":" and "\@" with "@". static std::string unescape(std::string_view s) { return boost::regex_replace(std::string(s), boost::regex("\\\\([@:])"), "$1"); } // Replace ":" with "\:" and "@" with "\@". static std::string escape(std::string_view s) { return boost::regex_replace(std::string(s), boost::regex("[@:]"), "\\\\$0"); } // Concat list of bytes into a single bytes. static bytes concat_fields(const std::vector& fields, const std::vector field_len) { std::size_t result_size = 4 * fields.size(); for (int32_t len : field_len) { result_size += len > 0 ? len : 0; } bytes result{bytes::initialized_later(), result_size}; bytes::iterator it = result.begin(); for (std::size_t i = 0; i < fields.size(); ++i) { int32_t tmp = net::hton(field_len[i]); it = std::copy_n(reinterpret_cast(&tmp), sizeof(tmp), it); if (field_len[i] > 0) { it = std::copy(std::begin(fields[i]), std::end(fields[i]), it); } } return result; } size_t abstract_type::hash(bytes_view v) const { return hash(managed_bytes_view(v)); } size_t abstract_type::hash(managed_bytes_view v) const { struct visitor { managed_bytes_view v; size_t operator()(const reversed_type_impl& t) { return t.underlying_type()->hash(v); } size_t operator()(const counter_type_impl&) { return long_type->hash(v); } size_t operator()(const abstract_type& t) { return std::hash()(v); } size_t operator()(const tuple_type_impl& t) { auto apply_hash = [] (auto&& type_value) { auto&& type = std::get<0>(type_value); auto&& value = std::get<1>(type_value); return value ? type->hash(*value) : 0; }; // FIXME: better accumulation function return std::ranges::fold_left(std::views::zip(t.all_types(), t.make_range(v)) | std::views::transform(apply_hash), 0, std::bit_xor<>()); } size_t operator()(const varint_type_impl& t) { return std::hash()(with_linearized(v, [&] (bytes_view bv) { return t.to_string(bv); })); } size_t operator()(const decimal_type_impl& t) { return std::hash()(with_linearized(v, [&] (bytes_view bv) { return t.to_string(bv); })); } size_t operator()(const empty_type_impl&) { return 0; } }; return visit(*this, visitor{v}); } static size_t concrete_serialized_size(const byte_type_impl::native_type&) { return sizeof(int8_t); } static size_t concrete_serialized_size(const short_type_impl::native_type&) { return sizeof(int16_t); } static size_t concrete_serialized_size(const int32_type_impl::native_type&) { return sizeof(int32_t); } static size_t concrete_serialized_size(const long_type_impl::native_type&) { return sizeof(int64_t); } static size_t concrete_serialized_size(const float_type_impl::native_type&) { return sizeof(float); } static size_t concrete_serialized_size(const double_type_impl::native_type&) { return sizeof(double); } static size_t concrete_serialized_size(const boolean_type_impl::native_type&) { return 1; } static size_t concrete_serialized_size(const timestamp_date_base_class::native_type&) { return 8; } static size_t concrete_serialized_size(const timeuuid_type_impl::native_type&) { return 16; } static size_t concrete_serialized_size(const simple_date_type_impl::native_type&) { return 4; } static size_t concrete_serialized_size(const string_type_impl::native_type& v) { return v.size(); } static size_t concrete_serialized_size(const bytes_type_impl::native_type& v) { return v.size(); } static size_t concrete_serialized_size(const inet_addr_type_impl::native_type& v) { return v.get().size(); } static size_t concrete_serialized_size_aux(const boost::multiprecision::cpp_int& num) { if (num) { return align_up(boost::multiprecision::msb(num) + 2, 8u) / 8; } else { return 1; } } static size_t concrete_serialized_size(const boost::multiprecision::cpp_int& num) { if (num < 0) { return concrete_serialized_size_aux(-num - 1); } return concrete_serialized_size_aux(num); } static size_t concrete_serialized_size(const utils::multiprecision_int& num) { return concrete_serialized_size(static_cast(num)); } static size_t concrete_serialized_size(const varint_type_impl::native_type& v) { return concrete_serialized_size(v.get()); } static size_t concrete_serialized_size(const decimal_type_impl::native_type& v) { const boost::multiprecision::cpp_int& uv = v.get().unscaled_value(); return sizeof(int32_t) + concrete_serialized_size(uv); } static size_t concrete_serialized_size(const duration_type_impl::native_type& v) { const auto& d = v.get(); return signed_vint::serialized_size(d.months) + signed_vint::serialized_size(d.days) + signed_vint::serialized_size(d.nanoseconds); } static size_t concrete_serialized_size(const tuple_type_impl::native_type& v) { size_t len = 0; for (auto&& e : v) { len += 4 + e.serialized_size(); } return len; } static size_t serialized_size(const abstract_type& t, const void* value); namespace { struct serialized_size_visitor { size_t operator()(const reversed_type_impl& t, const void* v) { return serialized_size(*t.underlying_type(), v); } size_t operator()(const counter_type_impl&, const void* v) { return serialized_size(*long_type, v); } size_t operator()(const empty_type_impl&, const void*) { return 0; } template size_t operator()(const concrete_type& t, const typename concrete_type::native_type* v) { if (v->empty()) { return 0; } return concrete_serialized_size(*v); } size_t operator()(const map_type_impl& t, const map_type_impl::native_type* v) { return map_serialized_size(v); } size_t operator()(const concrete_type, listlike_collection_type_impl>& t, const std::vector* v) { return listlike_serialized_size(v); } size_t operator()(const vector_type_impl& t, const vector_type_impl::native_type* v) { return vector_serialized_size(v); } }; } static size_t serialized_size(const abstract_type& t, const void* value) { return visit(t, value, serialized_size_visitor{}); } template static bytes serialize_value(const T& t, const typename T::native_type& v) { bytes b(bytes::initialized_later(), serialized_size_visitor{}(t, &v)); auto i = b.begin(); serialize_visitor{i}(t, &v); return b; } seastar::net::inet_address inet_addr_type_impl::from_string_view(std::string_view s) { try { return inet_address(std::string(s.data(), s.size())); } catch (...) { throw marshal_exception(seastar::format("Failed to parse inet_addr from '{}'", s)); } } utils::UUID uuid_type_impl::from_string_view(std::string_view s) { static const boost::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 (!boost::regex_match(s.begin(), s.end(), re)) { throw marshal_exception(seastar::format("Cannot parse uuid from '{}'", s)); } return utils::UUID(s); } utils::UUID timeuuid_type_impl::from_string_view(std::string_view s) { static const boost::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 (!boost::regex_match(s.begin(), s.end(), re)) { throw marshal_exception(seastar::format("Invalid UUID format ({})", s)); } utils::UUID v(s); if (v.version() != 1) { throw marshal_exception(format("Unsupported UUID version ({:d})", v.version())); } return v; } namespace { struct from_string_visitor { std::string_view s; bytes operator()(const reversed_type_impl& r) { return r.underlying_type()->from_string(s); } bytes operator()(const counter_type_impl&) { return long_type->from_string(s); } template bytes operator()(const integer_type_impl& t) { return decompose_value(parse_int(t, s)); } bytes operator()(const ascii_type_impl&) { auto bv = bytes_view(reinterpret_cast(s.begin()), s.size()); if (utils::ascii::validate(bv)) { return to_bytes(bv); } else { throw marshal_exception(seastar::format("Invalid ASCII character in string literal: '{}'", s)); } } bytes operator()(const string_type_impl&) { return to_bytes(bytes_view(reinterpret_cast(s.begin()), s.size())); } bytes operator()(const bytes_type_impl&) { return from_hex(s); } bytes operator()(const boolean_type_impl& t) { sstring s_lower(s.begin(), s.end()); std::transform(s_lower.begin(), s_lower.end(), s_lower.begin(), ::tolower); bool v; if (s.empty() || s_lower == "false") { v = false; } else if (s_lower == "true") { v = true; } else { throw marshal_exception(seastar::format("unable to make boolean from '{}'", s)); } return serialize_value(t, v); } bytes operator()(const timeuuid_type_impl&) { if (s.empty()) { return bytes(); } return timeuuid_type_impl::from_string_view(s).serialize(); } bytes operator()(const timestamp_date_base_class& t) { if (s.empty()) { return bytes(); } return serialize_value(t, timestamp_type_impl::from_string_view(s)); } bytes operator()(const simple_date_type_impl& t) { if (s.empty()) { return bytes(); } return serialize_value(t, simple_date_type_impl::from_string_view(s)); } bytes operator()(const time_type_impl& t) { if (s.empty()) { return bytes(); } return serialize_value(t, time_type_impl::from_string_view(s)); } bytes operator()(const uuid_type_impl&) { if (s.empty()) { return bytes(); } return uuid_type_impl::from_string_view(s).serialize(); } template bytes operator()(const floating_type_impl& t) { if (s.empty()) { return bytes(); } try { auto d = boost::lexical_cast(s.begin(), s.size()); return serialize_value(t, d); } catch (const boost::bad_lexical_cast& e) { throw marshal_exception(seastar::format("Invalid number format '{}'", s)); } } bytes operator()(const varint_type_impl& t) { if (s.empty()) { return bytes(); } try { std::string str(s.begin(), s.end()); varint_type_impl::native_type num(str); return serialize_value(t, num); } catch (...) { throw marshal_exception(seastar::format("unable to make int from '{}'", s)); } } bytes operator()(const decimal_type_impl& t) { if (s.empty()) { return bytes(); } try { decimal_type_impl::native_type bd(s); return serialize_value(t, bd); } catch (...) { throw marshal_exception(seastar::format("unable to make BigDecimal from '{}'", s)); } } bytes operator()(const duration_type_impl& t) { if (s.empty()) { return bytes(); } try { return serialize_value(t, cql_duration(s)); } catch (cql_duration_error const& e) { throw marshal_exception(e.what()); } } bytes operator()(const empty_type_impl&) { return bytes(); } bytes operator()(const inet_addr_type_impl& t) { // FIXME: support host names if (s.empty()) { return bytes(); } return serialize_value(t, t.from_string_view(s)); } bytes operator()(const tuple_type_impl& t) { std::vector field_strings = split_field_strings(s); if (field_strings.size() > t.size()) { throw marshal_exception( format("Invalid tuple literal: too many elements. Type {} expects {:d} but got {:d}", t.as_cql3_type(), t.size(), field_strings.size())); } std::vector fields(field_strings.size()); std::vector field_len(field_strings.size(), -1); for (std::size_t i = 0; i < field_strings.size(); ++i) { if (field_strings[i] != "@") { std::string field_string = unescape(field_strings[i]); fields[i] = t.type(i)->from_string(field_string); field_len[i] = fields[i].size(); } } return concat_fields(fields, field_len); } bytes operator()(const vector_type_impl& t) { // FIXME: abort(); return bytes(); } bytes operator()(const collection_type_impl&) { // FIXME: abort(); return bytes(); } }; } bytes abstract_type::from_string(std::string_view s) const { return visit(*this, from_string_visitor{s}); } static sstring tuple_to_string(const tuple_type_impl &t, const tuple_type_impl::native_type& b) { std::ostringstream out; for (size_t i = 0; i < b.size(); ++i) { if (i > 0) { out << ":"; } const auto& val = b[i]; if (val.is_null()) { out << "@"; } else { // We use ':' as delimiter and '@' to represent null, so they need to be escaped in the tuple's fields. auto typ = t.type(i); out << escape(typ->to_string(typ->decompose(val))); } } return std::move(out).str(); } template static sstring format_if_not_empty( const concrete_type& type, const typename concrete_type::native_type* b, F&& f) { if (b->empty()) { return {}; } return f(static_cast(*b)); } sstring timestamp_to_json_string(const timestamp_date_base_class& t, const bytes_view& bv) { auto tp = value_cast(t.deserialize(bv)); return format_if_not_empty(t, &tp, [](const db_clock::time_point& v) { return time_point_to_string(v, false); }); } static sstring to_string_impl(const abstract_type& t, const void* v); namespace { struct to_string_impl_visitor { template sstring operator()(const concrete_type& t, const typename concrete_type::native_type* v) { return format_if_not_empty(t, v, [] (const T& v) { return to_sstring(v); }); } sstring operator()(const bytes_type_impl& b, const bytes* v) { return format_if_not_empty(b, v, [] (const bytes& v) { return to_hex(v); }); } sstring operator()(const boolean_type_impl& b, const boolean_type_impl::native_type* v) { return format_if_not_empty(b, v, [] (const bool b) { return fmt::to_string(b); }); } sstring operator()(const timestamp_date_base_class& d, const timestamp_date_base_class::native_type* v) { return format_if_not_empty(d, v, [] (const db_clock::time_point& v) { return time_point_to_string(v); }); } sstring operator()(const decimal_type_impl& d, const decimal_type_impl::native_type* v) { return format_if_not_empty(d, v, [] (const big_decimal& v) { return v.to_string(); }); } sstring operator()(const duration_type_impl& d, const duration_type_impl::native_type* v) { return format_if_not_empty(d, v, [] (const cql_duration& v) { return ::to_string(v); }); } sstring operator()(const empty_type_impl&, const void*) { return sstring(); } sstring operator()(const inet_addr_type_impl& a, const inet_addr_type_impl::native_type* v) { return format_if_not_empty(a, v, [] (const seastar::net::inet_address& addr) { return fmt::to_string(addr); }); } sstring operator()(const list_type_impl& l, const list_type_impl::native_type* v) { return format_if_not_empty( l, v, [] (const list_type_impl::native_type& v) { return vector_to_string(v, ", "); }); } sstring operator()(const set_type_impl& s, const set_type_impl::native_type* v) { return format_if_not_empty(s, v, [] (const set_type_impl::native_type& v) { return vector_to_string(v, "; "); }); } sstring operator()(const map_type_impl& m, const map_type_impl::native_type* v) { return format_if_not_empty( m, v, [&m] (const map_type_impl::native_type& v) { return map_to_string(v, !m.is_multi_cell()); }); } sstring operator()(const reversed_type_impl& r, const void* v) { return to_string_impl(*r.underlying_type(), v); } sstring operator()(const counter_type_impl& c, const void* v) { return to_string_impl(*long_type, v); } sstring operator()(const simple_date_type_impl& s, const simple_date_type_impl::native_type* v) { return format_if_not_empty(s, v, simple_date_to_string); } sstring operator()(const string_type_impl& s, const string_type_impl::native_type* v) { return format_if_not_empty(s, v, [] (const sstring& s) { return s; }); } sstring operator()(const time_type_impl& t, const time_type_impl::native_type* v) { return format_if_not_empty(t, v, time_to_string); } sstring operator()(const timeuuid_type_impl& t, const timeuuid_type_impl::native_type* v) { return format_if_not_empty(t, v, [] (const utils::UUID& v) { return fmt::to_string(v); }); } sstring operator()(const tuple_type_impl& t, const tuple_type_impl::native_type* v) { return format_if_not_empty(t, v, [&t] (const tuple_type_impl::native_type& b) { return tuple_to_string(t, b); }); } sstring operator()(const vector_type_impl& vt, const vector_type_impl::native_type* v) { return format_if_not_empty( vt, v, [] (const vector_type_impl::native_type& v) { return vector_to_string(v, ", "); }); } sstring operator()(const uuid_type_impl& u, const uuid_type_impl::native_type* v) { return format_if_not_empty(u, v, [] (const utils::UUID& v) { return fmt::to_string(v); }); } sstring operator()(const varint_type_impl& t, const varint_type_impl::native_type* v) { return format_if_not_empty(t, v, [] (const utils::multiprecision_int& v) { return v.str(); }); } }; } static sstring to_string_impl(const abstract_type& t, const void* v) { return visit(t, v, to_string_impl_visitor{}); } sstring abstract_type::to_string_impl(const data_value& v) const { return ::to_string_impl(*this, get_value_ptr(v)); } static bool check_compatibility(const tuple_type_impl &t, const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const) { auto* x = dynamic_cast(&previous); if (!x) { return false; } auto c = std::mismatch( t.all_types().begin(), t.all_types().end(), x->all_types().begin(), x->all_types().end(), [predicate] (data_type a, data_type b) { return ((*a).*predicate)(*b); }); return c.second == x->all_types().end(); // this allowed to be longer } static bool check_compatibility(const vector_type_impl &t, const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const) { auto* x = dynamic_cast(&previous); if (!x) { return false; } return ((*t.get_elements_type()).*predicate)(*x->get_elements_type()) && t.get_dimension() == x->get_dimension(); } static bool is_value_compatible_with_internal_aux(const user_type_impl& t, const abstract_type& previous) { if (&t == &previous) { return true; } if (!previous.is_user_type()) { return false; } auto& x = static_cast(previous); if (t.is_multi_cell() != x.is_multi_cell() || t._keyspace != x._keyspace) { return false; } auto c = std::mismatch( t.all_types().begin(), t.all_types().end(), x.all_types().begin(), x.all_types().end(), [] (const data_type& a, const data_type& b) { return a->is_compatible_with(*b); }); return c.second == x.all_types().end(); // `this` allowed to have additional fields } static bool is_date_long_or_timestamp(const abstract_type& t) { auto k = t.get_kind(); return k == abstract_type::kind::long_kind || k == abstract_type::kind::date || k == abstract_type::kind::timestamp; } // Needed to handle ReversedType in value-compatibility checks. static bool is_value_compatible_with_internal(const abstract_type& t, const abstract_type& other) { struct visitor { const abstract_type& other; bool operator()(const abstract_type& t) { return t.is_compatible_with(other); } bool operator()(const long_type_impl& t) { return is_date_long_or_timestamp(other); } bool operator()(const timestamp_date_base_class& t) { return is_date_long_or_timestamp(other); } bool operator()(const uuid_type_impl&) { return other.get_kind() == abstract_type::kind::uuid || other.get_kind() == abstract_type::kind::timeuuid; } bool operator()(const varint_type_impl& t) { return other == t || int32_type->is_value_compatible_with(other) || long_type->is_value_compatible_with(other) || short_type->is_value_compatible_with(other) || byte_type->is_value_compatible_with(other); } bool operator()(const user_type_impl& t) { return is_value_compatible_with_internal_aux(t, other); } bool operator()(const tuple_type_impl& t) { return check_compatibility(t, other, &abstract_type::is_value_compatible_with); } bool operator()(const vector_type_impl& t) { return check_compatibility(t, other, &abstract_type::is_value_compatible_with); } bool operator()(const collection_type_impl& t) { return is_value_compatible_with_internal_aux(t, other); } bool operator()(const bytes_type_impl& t) { return true; } bool operator()(const reversed_type_impl& t) { return t.underlying_type()->is_value_compatible_with(other); } }; return visit(t, visitor{other}); } bool abstract_type::is_value_compatible_with(const abstract_type& other) const { return is_value_compatible_with_internal(*this, *other.underlying_type()); } std::optional user_type_impl::idx_of_field(const bytes_view& name) const { for (size_t i = 0; i < _field_names.size(); ++i) { if (name == bytes_view(_field_names[i])) { return {i}; } } return {}; } shared_ptr user_type_impl::get_instance(sstring keyspace, bytes name, std::vector field_names, std::vector field_types, bool multi_cell) { return intern::get_instance(std::move(keyspace), std::move(name), std::move(field_names), std::move(field_types), multi_cell); } sstring tuple_type_impl::make_name(const std::vector& types) { // To keep format compatibility with Origin we never wrap // tuple name into // "org.apache.cassandra.db.marshal.FrozenType(...)". // Even when the tuple is frozen. // For more details see #4087 return seastar::format("org.apache.cassandra.db.marshal.TupleType({})", fmt::join(types | std::views::transform(std::mem_fn(&abstract_type::name)), ", ")); } static std::optional> update_types(const std::vector types, const user_type updated) { std::optional> new_types = std::nullopt; for (uint32_t i = 0; i < types.size(); ++i) { auto&& ut = types[i]->update_user_type(updated); if (ut) { if (!new_types) { new_types = types; } (*new_types)[i] = std::move(*ut); } } return new_types; } static std::optional update_user_type_aux( const tuple_type_impl& t, const shared_ptr updated) { if (auto new_types = update_types(t.all_types(), updated)) { return std::make_optional(tuple_type_impl::get_instance(std::move(*new_types))); } return std::nullopt; } namespace { struct native_value_clone_visitor { const void* from; void* operator()(const reversed_type_impl& t) { return visit(*t.underlying_type(), native_value_clone_visitor{from}); } void* operator()(const counter_type_impl& t) { return visit(*long_type, native_value_clone_visitor{from}); } template void* operator()(const concrete_type&) { using nt = typename concrete_type::native_type; return new nt(*reinterpret_cast(from)); } void* operator()(const empty_type_impl&) { return new empty_type_representation(); } }; } void* abstract_type::native_value_clone(const void* from) const { return visit(*this, native_value_clone_visitor{from}); } namespace { struct native_value_delete_visitor { void* object; template void operator()(const concrete_type&) { delete reinterpret_cast::native_type*>(object); } void operator()(const reversed_type_impl& t) { return visit(*t.underlying_type(), native_value_delete_visitor{object}); } void operator()(const counter_type_impl& t) { return visit(*long_type, native_value_delete_visitor{object}); } void operator()(const empty_type_impl&) { delete reinterpret_cast(object); } }; } static void native_value_delete(const abstract_type& t, void* object) { visit(t, native_value_delete_visitor{object}); } namespace { struct native_typeid_visitor { template const std::type_info& operator()(const concrete_type&) { return typeid(typename concrete_type::native_type); } const std::type_info& operator()(const reversed_type_impl& t) { return visit(*t.underlying_type(), native_typeid_visitor{}); } const std::type_info& operator()(const counter_type_impl& t) { return visit(*long_type, native_typeid_visitor{}); } const std::type_info& operator()(const empty_type_impl&) { // Can't happen abort(); } }; } const std::type_info& abstract_type::native_typeid() const { return visit(*this, native_typeid_visitor{}); } bytes abstract_type::decompose(const data_value& value) const { if (!value._value) { return {}; } bytes b(bytes::initialized_later(), serialized_size(*this, value._value)); auto i = b.begin(); value.serialize(i); return b; } bool operator==(const data_value& x, const data_value& y) { if (x._type != y._type) { return false; } if (x.is_null() && y.is_null()) { return true; } if (x.is_null() || y.is_null()) { return false; } return x._type->equal(*x.serialize(), *y.serialize()); } size_t data_value::serialized_size() const { if (!_value) { return 0; } return ::serialized_size(*_type, _value); } void data_value::serialize(bytes::iterator& out) const { if (_value) { ::serialize(*_type, _value, out); } } bytes_opt data_value::serialize() const { if (!_value) { return std::nullopt; } bytes b(bytes::initialized_later(), serialized_size()); auto i = b.begin(); serialize(i); return b; } bytes data_value::serialize_nonnull() const { if (!_value) { on_internal_error(tlogger, "serialize_nonnull called on null"); } return std::move(*serialize()); } sstring abstract_type::get_string(const bytes& b) const { struct visitor { const bytes& b; sstring operator()(const abstract_type& t) { t.validate(b); return t.to_string(b); } sstring operator()(const reversed_type_impl& r) { return r.underlying_type()->get_string(b); } }; return visit(*this, visitor{b}); } sstring user_type_impl::get_name_as_string() const { auto real_utf8_type = static_cast(utf8_type.get()); return ::deserialize_value(*real_utf8_type, _name); } sstring user_type_impl::get_name_as_cql_string() const { return cql3::util::maybe_quote(get_name_as_string()); } cql3::description user_type_impl::describe(cql3::with_create_statement with_create_statement) const { auto maybe_create_statement = std::invoke([&] -> std::optional { if (!with_create_statement) { return std::nullopt; } fragmented_ostringstream os; os << "CREATE TYPE " << cql3::util::maybe_quote(_keyspace) << "." << get_name_as_cql_string() << " (\n"; for (size_t i = 0; i < _string_field_names.size(); i++) { os << " " << cql3::util::maybe_quote(_string_field_names[i]) << " " << _types[i]->cql3_type_name(); if (i < _string_field_names.size() - 1) { os << ","; } os << "\n"; } os << ");"; return std::move(os).to_managed_string(); }); return cql3::description { .keyspace = _keyspace, .type = "type", .name = get_name_as_string(), .create_statement = std::move(maybe_create_statement) }; } data_type user_type_impl::freeze() const { if (_is_multi_cell) { return get_instance(_keyspace, _name, _field_names, _types, false); } else { return shared_from_this(); } } sstring user_type_impl::make_name(sstring keyspace, bytes name, std::vector field_names, std::vector field_types, bool is_multi_cell) { std::ostringstream os; if (!is_multi_cell) { os << "org.apache.cassandra.db.marshal.FrozenType("; } os << "org.apache.cassandra.db.marshal.UserType(" << 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 << ")"; if (!is_multi_cell) { os << ")"; } return std::move(os).str(); } static std::optional update_user_type_aux( const user_type_impl& u, const shared_ptr updated) { if (u._keyspace == updated->_keyspace && u._name == updated->_name) { return std::make_optional(u.is_multi_cell() ? updated : updated->freeze()); } if (auto new_types = update_types(u.all_types(), updated)) { return std::make_optional( user_type_impl::get_instance(u._keyspace, u._name, u.field_names(), std::move(*new_types), u.is_multi_cell())); } return std::nullopt; } std::optional abstract_type::update_user_type(const shared_ptr updated) const { struct visitor { const shared_ptr updated; std::optional operator()(const abstract_type&) { return std::nullopt; } std::optional operator()(const empty_type_impl&) { return std::nullopt; } std::optional operator()(const reversed_type_impl& r) { return r.underlying_type()->update_user_type(updated); } std::optional operator()(const user_type_impl& u) { return update_user_type_aux(u, updated); } std::optional operator()(const tuple_type_impl& t) { return update_user_type_aux(t, updated); } std::optional operator()(const map_type_impl& m) { return update_user_type_aux(m, updated); } std::optional operator()(const set_type_impl& s) { return update_listlike(s, set_type_impl::get_instance, updated); } std::optional operator()(const list_type_impl& l) { return update_listlike(l, list_type_impl::get_instance, updated); } std::optional operator()(const vector_type_impl& v) { return update_user_type_aux(v, updated); } }; return visit(*this, visitor{updated}); } static bytes_ostream serialize_for_cql_aux(const map_type_impl&, collection_mutation_view_description mut) { bytes_ostream out; auto len_slot = out.write_place_holder(collection_size_len()); int elements = 0; for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb, false)) { write_collection_value(out, atomic_cell_value_view(e.first)); write_collection_value(out, e.second.value()); elements += 1; } } write_collection_size(len_slot, elements); return out; } static bytes_ostream serialize_for_cql_aux(const set_type_impl&, collection_mutation_view_description mut) { bytes_ostream out; auto len_slot = out.write_place_holder(collection_size_len()); int elements = 0; for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb, false)) { write_collection_value(out, atomic_cell_value_view(e.first)); elements += 1; } } write_collection_size(len_slot, elements); return out; } static bytes_ostream serialize_for_cql_aux(const list_type_impl&, collection_mutation_view_description mut) { bytes_ostream out; auto len_slot = out.write_place_holder(collection_size_len()); int elements = 0; for (auto&& e : mut.cells) { if (e.second.is_live(mut.tomb, false)) { write_collection_value(out, e.second.value()); elements += 1; } } write_collection_size(len_slot, elements); return out; } static bytes_ostream serialize_for_cql_aux(const user_type_impl& type, collection_mutation_view_description mut) { SCYLLA_ASSERT(type.is_multi_cell()); SCYLLA_ASSERT(mut.cells.size() <= type.size()); bytes_ostream out; size_t curr_field_pos = 0; for (auto&& e : mut.cells) { auto field_pos = deserialize_field_index(e.first); SCYLLA_ASSERT(field_pos < type.size()); // Some fields don't have corresponding cells -- these fields are null. while (curr_field_pos < field_pos) { write_simple(out, int32_t(-1)); ++curr_field_pos; } if (e.second.is_live(mut.tomb, false)) { auto value = e.second.value(); write_simple(out, int32_t(value.size_bytes())); for (auto&& frag : fragment_range(value)) { out.write(frag); } } else { write_simple(out, int32_t(-1)); } ++curr_field_pos; } // Trailing null fields while (curr_field_pos < type.size()) { write_simple(out, int32_t(-1)); ++curr_field_pos; } return out; } bytes_ostream serialize_for_cql(const abstract_type& type, collection_mutation_view v) { SCYLLA_ASSERT(type.is_multi_cell()); return v.with_deserialized(type, [&] (collection_mutation_view_description mv) { return visit(type, make_visitor( [&] (const map_type_impl& ctype) { return serialize_for_cql_aux(ctype, std::move(mv)); }, [&] (const set_type_impl& ctype) { return serialize_for_cql_aux(ctype, std::move(mv)); }, [&] (const list_type_impl& ctype) { return serialize_for_cql_aux(ctype, std::move(mv)); }, [&] (const user_type_impl& utype) { return serialize_for_cql_aux(utype, std::move(mv)); }, [&] (const abstract_type& o) -> bytes_ostream { throw std::runtime_error(format("attempted to serialize a collection of cells with type: {}", o.name())); } )); }); } bytes serialize_field_index(size_t idx) { if (idx >= size_t(std::numeric_limits::max())) { // should've been rejected earlier, but just to be sure... throw std::runtime_error(format("index for user type field too large: {}", idx)); } bytes b(bytes::initialized_later(), sizeof(int16_t)); write_be(reinterpret_cast(b.data()), static_cast(idx)); return b; } size_t deserialize_field_index(const bytes_view& b) { SCYLLA_ASSERT(b.size() == sizeof(int16_t)); return read_be(reinterpret_cast(b.data())); } size_t deserialize_field_index(managed_bytes_view b) { SCYLLA_ASSERT(b.size_bytes() == sizeof(int16_t)); return be_to_cpu(read_simple_native(b)); } thread_local const shared_ptr byte_type(make_shared()); thread_local const shared_ptr short_type(make_shared()); 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()); thread_local const shared_ptr bytes_type(make_shared()); thread_local const shared_ptr utf8_type(make_shared()); 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 simple_date_type(make_shared()); thread_local const shared_ptr time_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 shared_ptr varint_type(make_shared()); thread_local const shared_ptr decimal_type(make_shared()); thread_local const shared_ptr counter_type(make_shared()); thread_local const shared_ptr duration_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 = { { byte_type_name, byte_type }, { short_type_name, short_type }, { int32_type_name, int32_type }, { long_type_name, long_type }, { ascii_type_name, ascii_type }, { bytes_type_name, bytes_type }, { utf8_type_name, utf8_type }, { boolean_type_name, boolean_type }, { date_type_name, date_type }, { timeuuid_type_name, timeuuid_type }, { timestamp_type_name, timestamp_type }, { simple_date_type_name, simple_date_type }, { time_type_name, time_type }, { uuid_type_name, uuid_type }, { inet_addr_type_name, inet_addr_type }, { float_type_name, float_type }, { double_type_name, double_type }, { varint_type_name, varint_type }, { decimal_type_name, decimal_type }, { counter_type_name, counter_type }, { duration_type_name, duration_type }, { empty_type_name, empty_type }, }; auto it = types.find(name); if (it == types.end()) { throw std::invalid_argument(format("unknown type: {}\n", name)); } return it->second; } data_value::~data_value() { if (_value) { native_value_delete(*_type, _value); } } data_value::data_value(const data_value& v) : _value(nullptr), _type(v._type) { if (v._value) { _value = _type->native_value_clone(v._value); } } data_value& data_value::operator=(data_value&& x) { auto tmp = std::move(x); std::swap(tmp._value, this->_value); std::swap(tmp._type, this->_type); return *this; } data_value::data_value(bytes v) : data_value(make_new(bytes_type, v)) { } data_value::data_value(sstring&& v) : data_value(make_new(utf8_type, std::move(v))) { } data_value::data_value(const char* v) : data_value(std::string_view(v)) { } data_value::data_value(std::string_view v) : data_value(sstring(v)) { } data_value::data_value(const std::string& v) : data_value(std::string_view(v)) { } data_value::data_value(const sstring& v) : data_value(std::string_view(v)) { } data_value::data_value(ascii_native_type v) : data_value(make_new(ascii_type, v.string)) { } data_value::data_value(bool v) : data_value(make_new(boolean_type, v)) { } data_value::data_value(int8_t v) : data_value(make_new(byte_type, v)) { } data_value::data_value(int16_t v) : data_value(make_new(short_type, v)) { } data_value::data_value(int32_t v) : data_value(make_new(int32_type, v)) { } data_value::data_value(int64_t v) : data_value(make_new(long_type, v)) { } data_value::data_value(utils::UUID v) : data_value(make_new(uuid_type, v)) { } data_value::data_value(float v) : data_value(make_new(float_type, v)) { } data_value::data_value(double v) : data_value(make_new(double_type, v)) { } data_value::data_value(seastar::net::inet_address v) : data_value(make_new(inet_addr_type, v)) { } data_value::data_value(seastar::net::ipv4_address v) : data_value(seastar::net::inet_address(v)) { } data_value::data_value(seastar::net::ipv6_address v) : data_value(seastar::net::inet_address(v)) { } data_value::data_value(simple_date_native_type v) : data_value(make_new(simple_date_type, v.days)) { } data_value::data_value(db_clock::time_point v) : data_value(make_new(timestamp_type, v)) { } data_value::data_value(time_native_type v) : data_value(make_new(time_type, v.nanoseconds)) { } data_value::data_value(timeuuid_native_type v) : data_value(make_new(timeuuid_type, v.uuid)) { } data_value::data_value(date_type_native_type v) : data_value(make_new(date_type, v.tp)) { } data_value::data_value(utils::multiprecision_int v) : data_value(make_new(varint_type, v)) { } data_value::data_value(big_decimal v) : data_value(make_new(decimal_type, v)) { } data_value::data_value(cql_duration d) : data_value(make_new(duration_type, d)) { } data_value::data_value(empty_type_representation e) : data_value(make_new(empty_type, e)) { } sstring data_value::to_parsable_string() const { // For some reason trying to do it using fmt::format refuses to compile // auto to_parsable_str_transform = std::views::transform([](const data_value& dv) -> sstring { // return dv.to_parsable_string(); // }); if (_type->without_reversed().is_list()) { const list_type_impl::native_type* the_list = (const list_type_impl::native_type*)_value; std::ostringstream result; result << "["; for (size_t i = 0; i < the_list->size(); i++) { if (i != 0) { result << ", "; } result << (*the_list)[i].to_parsable_string(); } result << "]"; return std::move(result).str(); //return fmt::format("[{}]", fmt::join(*the_list | to_parsable_str_transform, ", ")); } if (_type->without_reversed().is_set()) { const set_type_impl::native_type* the_set = (const set_type_impl::native_type*)_value; std::ostringstream result; result << "{"; for (size_t i = 0; i < the_set->size(); i++) { if (i != 0) { result << ", "; } result << (*the_set)[i].to_parsable_string(); } result << "}"; return std::move(result).str(); //return fmt::format("{{{}}}", fmt::join(*the_set | to_parsable_str_transform, ", ")); } if (_type->without_reversed().is_map()) { const map_type_impl::native_type* the_map = (const map_type_impl::native_type*)_value; std::ostringstream result; result << "{"; for (size_t i = 0; i < the_map->size(); i++) { if (i != 0) { result << ", "; } result << (*the_map)[i].first.to_parsable_string() << ":" << (*the_map)[i].second.to_parsable_string(); } result << "}"; return std::move(result).str(); //auto to_map_elem_transform = std::views::transform( // [](const std::pair& map_elem) -> sstring { // return fmt::format("{{{}:{}}}", map_elem.first.to_parsable_string(), map_elem.second.to_parsable_string()); // } //); // //return fmt::format("{{{}}}", fmt::join(*the_map | to_map_elem_transform, ", ")); } if (_type->without_reversed().is_user_type()) { const user_type_impl* user_typ = dynamic_cast(&_type->without_reversed()); const user_type_impl::native_type* field_values = (const user_type_impl::native_type*)_value; std::ostringstream result; result << "{"; for (std::size_t i = 0; i < field_values->size(); i++) { if (i != 0) { result << ", "; } result << user_typ->string_field_names().at(i) << ":" << (*field_values)[i].to_parsable_string(); } result << "}"; return std::move(result).str(); } if (_type->without_reversed().is_tuple()) { const tuple_type_impl::native_type* tuple_elements = (const tuple_type_impl::native_type*)_value; std::ostringstream result; result << "("; for (std::size_t i = 0; i < tuple_elements->size(); i++) { if (i != 0) { result << ", "; } result << (*tuple_elements)[i].to_parsable_string(); } result << ")"; return std::move(result).str(); } if (_type->without_reversed().is_vector()) { const vector_type_impl::native_type* vector_elements = (const vector_type_impl::native_type*)_value; std::ostringstream result; result << "["; for (std::size_t i = 0; i < vector_elements->size(); i++) { if (i != 0) { result << ", "; } result << (*vector_elements)[i].to_parsable_string(); } result << "]"; return std::move(result).str(); } abstract_type::kind type_kind = _type->without_reversed().get_kind(); if (type_kind == abstract_type::kind::utf8 || type_kind == abstract_type::kind::ascii || type_kind == abstract_type::kind::inet || type_kind == abstract_type::kind::time || type_kind == abstract_type::kind::date || type_kind == abstract_type::kind::timestamp ) { // Put quotes on types that require it return fmt::format("'{}'", *this); } // For simple types the default operator<< should work ok return fmt::format("{}", *this); } data_value make_list_value(data_type type, list_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } data_value make_set_value(data_type type, set_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } data_value make_map_value(data_type type, map_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } data_value make_tuple_value(data_type type, tuple_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } data_value make_vector_value(data_type type, vector_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } data_value make_user_value(data_type type, user_type_impl::native_type value) { return data_value::make_new(std::move(type), std::move(value)); } auto fmt::formatter::format(const data_value& v, fmt::format_context& ctx) const -> decltype(ctx.out()) { if (v.is_null()) { return fmt::format_to(ctx.out(), "null"); } return fmt::format_to(ctx.out(), "{}", v.type()->to_string_impl(v)); } shared_ptr reversed_type_impl::get_instance(data_type type) { return intern::get_instance(std::move(type)); } data_type reversed(data_type type) { if (type->is_reversed()) { return type->underlying_type(); } else { return reversed_type_impl::get_instance(type); } } bool abstract_type::contains_set_or_map() const { return _contains_set_or_map; } bool abstract_type::contains_collection() const { return _contains_collection; } bool abstract_type::bound_value_needs_to_be_reserialized() const { // If a value contains set or map, then this collection can be sent in the wrong order // or there could be duplicates inside. We need to reserialize it into proper set or map. if (contains_set_or_map()) { return true; } return false; } // compile once the template instance that was externed in marshal_exception.hh namespace seastar { template void throw_with_backtrace(sstring&&); }