Files
scylladb/types.cc
Raphael S. Carvalho a6f8f4fe24 compaction: do not write expired cell as dead cell if it can be purged right away
When compacting a fully expired sstable, we're not allowing that sstable
to be purged because expired cell is *unconditionally* converted into a
dead cell. Why not check if the expired cell can be purged instead using
gc before and max purgeable timestamp?

Currently, we need two compactions to get rid of a fully expired sstable
which cells could have always been purged.

look at this sstable with expired cell:
  {
    "partition" : {
      "key" : [ "2" ],
      "position" : 0
    },
    "rows" : [
      {
        "type" : "row",
        "position" : 120,
        "liveness_info" : { "tstamp" : "2017-04-09T17:07:12.702597Z",
"ttl" : 20, "expires_at" : "2017-04-09T17:07:32Z", "expired" : true },
        "cells" : [
          { "name" : "country", "value" : "1" },
        ]

now this sstable data after first compaction:
[shard 0] compaction - Compacted 1 sstables to [...]. 120 bytes to 79
(~65% of original) in 229ms = 0.000328997MB/s.

  {
    ...
    "rows" : [
      {
        "type" : "row",
        "position" : 79,
        "cells" : [
          { "name" : "country", "deletion_info" :
{ "local_delete_time" : "2017-04-09T17:07:12Z" },
            "tstamp" : "2017-04-09T17:07:12.702597Z"
          },
        ]

now another compaction will actually get rid of data:
compaction - Compacted 1 sstables to []. 79 bytes to 0 (~0% of original)
in 1ms = 0MB/s. ~2 total partitions merged to 0

NOTE:
It's a waste of time to wait for second compaction because the expired
cell could have been purged at first compaction because it satisfied
gc_before and max purgeable timestamp.

Fixes #2249, #2253

Signed-off-by: Raphael S. Carvalho <raphaelsc@scylladb.com>
Message-Id: <20170413001049.9663-1-raphaelsc@scylladb.com>
2017-04-13 10:59:19 +03:00

3214 lines
108 KiB
C++

/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include "cql3/cql3_type.hh"
#include "cql3/lists.hh"
#include "cql3/maps.hh"
#include "cql3/sets.hh"
#include "types.hh"
#include "core/print.hh"
#include "net/ip.hh"
#include "database.hh"
#include "utils/serialization.hh"
#include "combine.hh"
#include <cmath>
#include <chrono>
#include <sstream>
#include <string>
#include <regex>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/numeric.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
#include <boost/locale/encoding_utf.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include "utils/big_decimal.hh"
#include "utils/date.h"
template<typename T>
sstring time_point_to_string(const T& tp)
{
auto timestamp = tp.time_since_epoch().count();
auto time = boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(timestamp);
return boost::posix_time::to_iso_extended_string(time);
}
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* empty_type_name = "org.apache.cassandra.db.marshal.EmptyType";
template<typename T>
struct simple_type_traits {
static T read_nonempty(bytes_view v) {
return read_simple_exactly<T>(v);
}
};
template<>
struct simple_type_traits<bool> {
static bool read_nonempty(bytes_view v) {
return read_simple_exactly<int8_t>(v) != 0;
}
};
template<>
struct simple_type_traits<db_clock::time_point> {
static db_clock::time_point read_nonempty(bytes_view v) {
return db_clock::time_point(db_clock::duration(read_simple_exactly<int64_t>(v)));
}
};
template <typename T>
struct simple_type_impl : concrete_type<T> {
simple_type_impl(sstring name) : concrete_type<T>(std::move(name)) {}
virtual int32_t compare(bytes_view v1, bytes_view v2) const override {
if (v1.empty()) {
return v2.empty() ? 0 : -1;
}
if (v2.empty()) {
return 1;
}
T a = simple_type_traits<T>::read_nonempty(v1);
T b = simple_type_traits<T>::read_nonempty(v2);
return a == b ? 0 : a < b ? -1 : 1;
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return compare(v1, v2) < 0;
}
virtual bool is_byte_order_equal() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual bool references_user_type(const sstring& keyspace, const bytes& name) const {
return false;
}
virtual std::experimental::optional<data_type> update_user_type(const shared_ptr<const user_type_impl> updated) const {
return std::experimental::nullopt;
}
};
template<typename T>
struct integer_type_impl : simple_type_impl<T> {
integer_type_impl(sstring name) : simple_type_impl<T>(name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto v1 = this->from_value(value);
if (v1.empty()) {
return;
}
auto v = v1.get();
auto u = net::hton(v);
out = std::copy_n(reinterpret_cast<const char*>(&u), sizeof(u), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
auto v = this->from_value(value);
if (v.empty()) {
return 0;
}
return sizeof(v.get());
}
virtual data_value deserialize(bytes_view v) const override {
auto x = read_simple_opt<T>(v);
if (!x) {
return this->make_empty();
} else {
return this->make_value(*x);
}
}
T compose_value(const bytes& b) const {
if (b.size() != sizeof(T)) {
throw marshal_exception();
}
return (T)net::ntoh(*reinterpret_cast<const T*>(b.begin()));
}
bytes decompose_value(T v) const {
bytes b(bytes::initialized_later(), sizeof(v));
*reinterpret_cast<T*>(b.begin()) = (T)net::hton(v);
return b;
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != sizeof(T)) {
throw marshal_exception();
}
}
T parse_int(sstring_view s) const {
try {
auto value64 = boost::lexical_cast<int64_t>(s.begin(), s.size());
auto value = static_cast<T>(value64);
if (value != value64) {
throw marshal_exception(sprint("Value out of range for type %s: '%s'", this->name(), s));
}
return static_cast<T>(value);
} catch (const boost::bad_lexical_cast& e) {
throw marshal_exception(sprint("Invalid number format '%s'", s));
}
}
virtual bytes from_string(sstring_view s) const override {
return decompose_value(parse_int(s));
}
virtual sstring to_string(const bytes& b) const override {
if (b.empty()) {
return {};
}
return to_sstring(compose_value(b));
}
};
struct byte_type_impl : integer_type_impl<int8_t> {
byte_type_impl() : integer_type_impl{byte_type_name}
{ }
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 1) {
throw marshal_exception(sprint("Expected 1 byte for a tinyint (%d)", v.size()));
}
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::tinyint;
}
};
struct short_type_impl : integer_type_impl<int16_t> {
short_type_impl() : integer_type_impl{short_type_name}
{ }
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 2) {
throw marshal_exception(sprint("Expected 2 bytes for a smallint (%d)", v.size()));
}
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::smallint;
}
};
struct int32_type_impl : integer_type_impl<int32_t> {
int32_type_impl() : integer_type_impl{int32_type_name}
{ }
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::int_;
}
};
struct long_type_impl : integer_type_impl<int64_t> {
long_type_impl() : integer_type_impl{long_type_name}
{ }
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::bigint;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return &other == this || &other == date_type.get() || &other == timestamp_type.get();
}
};
struct string_type_impl : public concrete_type<sstring> {
string_type_impl(sstring name)
: concrete_type(name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& v = from_value(value);
out = std::copy(v.begin(), v.end(), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
auto& v = from_value(value);
return v.size();
}
virtual data_value deserialize(bytes_view v) const override {
// FIXME: validation?
return make_value(std::make_unique<native_type>(reinterpret_cast<const char*>(v.begin()), v.size()));
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return less_unsigned(v1, v2);
}
virtual bool is_byte_order_equal() const override {
return true;
}
virtual bool is_byte_order_comparable() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual void validate(bytes_view v) const override {
if (as_cql3_type() == cql3::cql3_type::ascii) {
if (std::any_of(v.begin(), v.end(), [] (int8_t b) { return b < 0; })) {
throw marshal_exception();
}
} else {
try {
boost::locale::conv::utf_to_utf<char>(v.begin(), v.end(), boost::locale::conv::stop);
} catch (const boost::locale::conv::conversion_error& ex) {
throw marshal_exception(ex.what());
}
}
}
virtual bytes from_string(sstring_view s) const override {
return to_bytes(bytes_view(reinterpret_cast<const int8_t*>(s.begin()), s.size()));
}
virtual sstring to_string(const bytes& b) const override {
return sstring(reinterpret_cast<const char*>(b.begin()), b.size());
}
};
struct ascii_type_impl final : public string_type_impl {
ascii_type_impl() : string_type_impl(ascii_type_name) {}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::ascii;
}
};
struct utf8_type_impl final : public string_type_impl {
static const char* name;
utf8_type_impl() : string_type_impl(utf8_type_name) {}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::text;
}
virtual bool is_compatible_with(const abstract_type& other) const override {
// Anything that is ascii is also utf8, and they both use bytes
// comparison
return this == &other || &other == ascii_type.get();
}
using concrete_type::from_value;
};
struct bytes_type_impl final : public concrete_type<bytes> {
bytes_type_impl() : concrete_type(bytes_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& v = from_value(value);
out = std::copy(v.begin(), v.end(), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
auto& v = from_value(value);
return v.size();
}
virtual data_value deserialize(bytes_view v) const override {
return make_value(std::make_unique<native_type>(v.begin(), v.end()));
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return less_unsigned(v1, v2);
}
virtual bool is_byte_order_equal() const override {
return true;
}
virtual bool is_byte_order_comparable() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual bytes from_string(sstring_view s) const override {
return from_hex(s);
}
virtual sstring to_string(const bytes& b) const override {
return to_hex(b);
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::blob;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return true;
}
virtual bool is_compatible_with(const abstract_type& other) const override {
// Both asciiType and utf8Type really use bytes comparison and
// bytesType validate everything, so it is compatible with the former.
return this == &other || &other == ascii_type.get() || &other == utf8_type.get();
}
};
struct boolean_type_impl : public simple_type_impl<bool> {
boolean_type_impl() : simple_type_impl<bool>(boolean_type_name) {}
void serialize_value(maybe_empty<bool> value, bytes::iterator& out) const {
if (!value.empty()) {
*out++ = char(value);
}
}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
serialize_value(from_value(value), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
if (from_value(value).empty()) {
return 0;
}
return 1;
}
size_t serialized_size(bool value) const {
return 1;
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
if (v.size() != 1) {
throw marshal_exception();
}
return make_value(*v.begin() != 0);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 1) {
throw marshal_exception();
}
}
virtual bytes from_string(sstring_view s) const override {
sstring s_lower(s.begin(), s.end());
std::transform(s_lower.begin(), s_lower.end(), s_lower.begin(), ::tolower);
if (s.empty() || s_lower == "false") {
return ::serialize_value(*this, false);
} else if (s_lower == "true") {
return ::serialize_value(*this, true);
} else {
throw marshal_exception(sprint("unable to make boolean from '%s'", s));
}
}
virtual sstring to_string(const bytes& b) const override {
if (b.empty()) {
return "";
}
if (b.size() != 1) {
throw marshal_exception();
}
return *b.begin() ? "true" : "false";
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::boolean;
}
};
class date_type_impl : public concrete_type<db_clock::time_point> {
static logging::logger _logger;
public:
date_type_impl() : concrete_type(date_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& v = from_value(value);
if (v.empty()) {
return;
}
int64_t i = v.get().time_since_epoch().count();
i = net::hton(uint64_t(i));
out = std::copy_n(reinterpret_cast<const char*>(&i), sizeof(i), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 8;
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
auto tmp = read_simple_exactly<uint64_t>(v);
return make_value(db_clock::time_point(db_clock::duration(tmp)));
}
virtual bool less(bytes_view b1, bytes_view b2) const override {
return compare_unsigned(b1, b2);
}
virtual bool is_byte_order_comparable() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual bytes from_string(sstring_view s) const override;
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return time_point_to_string(from_value(v).get());
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::timestamp;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return &other == this || &other == timestamp_type.get() || &other == long_type.get();
}
virtual bool is_compatible_with(const abstract_type& other) const override {
if (&other == this) {
return true;
}
if (&other == timestamp_type.get()) {
_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;
}
};
logging::logger date_type_impl::_logger(date_type_name);
struct timeuuid_type_impl : public concrete_type<utils::UUID> {
timeuuid_type_impl() : concrete_type<utils::UUID>(timeuuid_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& uuid1 = from_value(value);
if (uuid1.empty()) {
return;
}
auto uuid = uuid1.get();
out = std::copy_n(uuid.to_bytes().begin(), sizeof(uuid), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 16;
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
uint64_t msb, lsb;
msb = read_simple<uint64_t>(v);
lsb = read_simple<uint64_t>(v);
if (!v.empty()) {
throw marshal_exception();
}
return make_value(utils::UUID(msb, lsb));
}
virtual bool less(bytes_view b1, bytes_view b2) const override {
if (b1.empty()) {
return b2.empty() ? false : true;
}
if (b2.empty()) {
return false;
}
auto r = compare_bytes(b1, b2);
if (r != 0) {
return r < 0;
} else {
return std::lexicographical_compare(b1.begin(), b1.end(), b2.begin(), b2.end());
}
}
virtual bool is_byte_order_equal() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 16) {
throw marshal_exception();
}
auto msb = read_simple<uint64_t>(v);
auto lsb = read_simple<uint64_t>(v);
utils::UUID uuid(msb, lsb);
if (uuid.version() != 1) {
throw marshal_exception();
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$");
if (!std::regex_match(s.begin(), s.end(), re)) {
throw marshal_exception();
}
utils::UUID v(s);
if (v.version() != 1) {
throw marshal_exception();
}
return v.to_bytes();
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return from_value(v).get().to_sstring();
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::timeuuid;
}
private:
static int compare_bytes(bytes_view o1, bytes_view o2) {
auto compare_pos = [&] (unsigned pos, int mask, int ifequal) {
int d = (o1[pos] & mask) - (o2[pos] & mask);
return d ? d : ifequal;
};
return compare_pos(6, 0xf,
compare_pos(7, 0xff,
compare_pos(4, 0xff,
compare_pos(5, 0xff,
compare_pos(0, 0xff,
compare_pos(1, 0xff,
compare_pos(2, 0xff,
compare_pos(3, 0xff, 0))))))));
}
friend class uuid_type_impl;
};
class timestamp_type_impl : public simple_type_impl<db_clock::time_point> {
static logging::logger _logger;
public:
timestamp_type_impl() : simple_type_impl(timestamp_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto&& v1 = from_value(value);
if (v1.empty()) {
return;
}
uint64_t v = v1.get().time_since_epoch().count();
v = net::hton(v);
out = std::copy_n(reinterpret_cast<const char*>(&v), sizeof(v), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 8;
}
virtual data_value deserialize(bytes_view in) const override {
if (in.empty()) {
return make_empty();
}
auto v = read_simple_exactly<uint64_t>(in);
return make_value(db_clock::time_point(db_clock::duration(v)));
}
// FIXME: isCompatibleWith(timestampuuid)
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != sizeof(uint64_t)) {
throw marshal_exception();
}
}
static boost::posix_time::ptime get_time(const std::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<int>(sm[1]);
auto month = boost::lexical_cast<int>(sm[2]);
auto day = boost::lexical_cast<int>(sm[3]);
boost::gregorian::date date(year, month, day);
auto hour = sm[5].length() ? boost::lexical_cast<int>(sm[5]) : 0;
auto minute = sm[6].length() ? boost::lexical_cast<int>(sm[6]) : 0;
auto second = sm[8].length() ? boost::lexical_cast<int>(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();
}
auto value = boost::lexical_cast<int>(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();
}
static int64_t timestamp_from_string(sstring_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;
}
std::regex date_re("^(\\d{4})-(\\d+)-(\\d+)([ t](\\d+):(\\d+)(:(\\d+)(\\.(\\d+))?)?)?");
std::smatch dsm;
if (!std::regex_search(str, dsm, date_re)) {
throw marshal_exception();
}
auto t = get_time(dsm);
auto tz = dsm.suffix().str();
std::regex tz_re("([\\+-]\\d{2}:?(\\d{2})?)");
std::smatch tsm;
if (std::regex_match(tz, tsm, tz_re)) {
t -= get_utc_offset(tsm.str());
} else if (tz.empty()) {
typedef boost::date_time::c_local_adjustor<boost::posix_time::ptime> local_tz;
// local_tz::local_to_utc(), where are you?
auto t1 = local_tz::utc_to_local(t);
auto tz_offset = t1 - t;
auto t2 = local_tz::utc_to_local(t - tz_offset);
auto dst_offset = t2 - t;
t -= tz_offset + dst_offset;
} else {
throw marshal_exception();
}
return (t - boost::posix_time::from_time_t(0)).total_milliseconds();
} catch (...) {
throw marshal_exception(sprint("unable to parse date '%s'", s));
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
int64_t ts = net::hton(timestamp_from_string(s));
bytes b(bytes::initialized_later(), sizeof(int64_t));
std::copy_n(reinterpret_cast<const int8_t*>(&ts), sizeof(ts), b.begin());
return b;
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return time_point_to_string(from_value(v).get());
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::timestamp;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return &other == this || &other == date_type.get() || &other == long_type.get();
}
virtual bool is_compatible_with(const abstract_type& other) const override {
if (&other == this) {
return true;
}
if (&other == date_type.get()) {
_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;
}
};
logging::logger timestamp_type_impl::_logger(timestamp_type_name);
struct simple_date_type_impl : public simple_type_impl<uint32_t> {
simple_date_type_impl() : simple_type_impl{simple_date_type_name}
{ }
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto&& v1 = from_value(value);
if (v1.empty()) {
return;
}
uint32_t v = v1.get();
v = net::hton(v);
out = std::copy_n(reinterpret_cast<const char*>(&v), sizeof(v), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 4;
}
virtual data_value deserialize(bytes_view in) const override {
if (in.empty()) {
return make_empty();
}
auto v = read_simple_exactly<uint32_t>(in);
return make_value(v);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 4) {
throw marshal_exception(sprint("Expected 4 byte long for date (%d)", v.size()));
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
uint32_t ts = net::hton(days_from_string(s));
bytes b(bytes::initialized_later(), sizeof(int32_t));
std::copy_n(reinterpret_cast<const int8_t*>(&ts), sizeof(ts), b.begin());
return b;
}
static uint32_t days_from_string(sstring_view s) {
std::string str;
str.resize(s.size());
std::transform(s.begin(), s.end(), str.begin(), ::tolower);
char* end;
auto v = std::strtoll(s.begin(), &end, 10);
if (end == s.begin() + s.size()) {
return v;
}
static std::regex date_re("^(-?\\d+)-(\\d+)-(\\d+)");
std::smatch dsm;
if (!std::regex_match(str, dsm, date_re)) {
throw marshal_exception(sprint("Unable to coerce '%s' to a formatted date (long)", str));
}
auto t = get_time(dsm);
return serialize(str, date::local_days(t).time_since_epoch().count());
}
static date::year_month_day get_time(const std::smatch& sm) {
auto year = boost::lexical_cast<long>(sm[1]);
auto month = boost::lexical_cast<unsigned>(sm[2]);
auto day = boost::lexical_cast<unsigned>(sm[3]);
return date::year_month_day{date::year{year}, date::month{month}, date::day{day}};
}
static uint32_t serialize(const std::string& input, int64_t days) {
if (days < std::numeric_limits<int32_t>::min()) {
throw marshal_exception(sprint("Input date %s is less than min supported date -5877641-06-23", input));
}
if (days > std::numeric_limits<int32_t>::max()) {
throw marshal_exception(sprint("Input date %s is greater than max supported date 5881580-07-11", input));
}
days += 1UL << 31;
return static_cast<uint32_t>(days);
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
date::days days{from_value(v).get() - (1UL << 31)};
date::year_month_day ymd{date::local_days{days}};
std::ostringstream str;
str << ymd;
return str.str();
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::date;
}
};
struct time_type_impl : public simple_type_impl<int64_t> {
time_type_impl() : simple_type_impl{time_type_name}
{ }
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto&& v1 = from_value(value);
if (v1.empty()) {
return;
}
uint64_t v = v1.get();
v = net::hton(v);
out = std::copy_n(reinterpret_cast<const char*>(&v), sizeof(v), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 8;
}
virtual data_value deserialize(bytes_view in) const override {
if (in.empty()) {
return make_empty();
}
auto v = read_simple_exactly<int64_t>(in);
return make_value(v);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 8) {
throw marshal_exception(sprint("Expected 8 byte long for time (%d)", v.size()));
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
int64_t ts = net::hton(parse_time(s));
bytes b(bytes::initialized_later(), sizeof(int64_t));
std::copy_n(reinterpret_cast<const int8_t*>(&ts), sizeof(ts), b.begin());
return b;
}
static int64_t parse_time(sstring_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(s.substr(0, hours_end).to_string());
if (hours < 0 || hours >= 24) {
throw marshal_exception("Hour out of bounds.");
}
auto minutes_end = s.find(':', hours_end+1);
if (minutes_end == std::string::npos) {
throw marshal_exception(format_error);
}
int64_t minutes = std::stol(s.substr(hours_end + 1, hours_end-minutes_end).to_string());
if (minutes < 0 || minutes >= 60) {
throw marshal_exception("Minute out of bounds.");
}
auto seconds_end = s.find('.', minutes_end+1);
if (seconds_end == std::string::npos) {
seconds_end = s.length();
}
int64_t seconds = std::stol(s.substr(minutes_end + 1, minutes_end-seconds_end).to_string());
if (seconds < 0 || seconds >= 60) {
throw marshal_exception("Second out of bounds.");
}
int64_t nanoseconds = 0;
if (seconds_end < s.length()) {
nanoseconds = std::stol(s.substr(seconds_end + 1).to_string());
nanoseconds *= std::pow(10, 9-(s.length() - (seconds_end + 1)));
if (nanoseconds < 0 || nanoseconds >= 1000 * 1000 * 1000) {
throw marshal_exception("Nanosecond out of bounds.");
}
}
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();
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
std::chrono::nanoseconds nanoseconds{from_value(v).get()};
auto time = date::make_time(nanoseconds);
std::ostringstream str;
str << time;
return str.str();
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::time;
}
};
struct uuid_type_impl : concrete_type<utils::UUID> {
uuid_type_impl() : concrete_type(uuid_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& uuid = from_value(value);
out = std::copy_n(uuid.get().to_bytes().begin(), sizeof(uuid.get()), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
return 16;
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
auto msb = read_simple<uint64_t>(v);
auto lsb = read_simple<uint64_t>(v);
if (!v.empty()) {
throw marshal_exception();
}
return make_value(utils::UUID(msb, lsb));
}
virtual bool less(bytes_view b1, bytes_view b2) const override {
if (b1.size() < 16) {
return b2.size() < 16 ? false : true;
}
if (b2.size() < 16) {
return false;
}
auto v1 = (b1[6] >> 4) & 0x0f;
auto v2 = (b2[6] >> 4) & 0x0f;
if (v1 != v2) {
return v1 < v2;
}
if (v1 == 1) {
auto c = timeuuid_type_impl::compare_bytes(b1, b2);
if (c) {
return c < 0;
}
}
return less_unsigned(b1, b2);
}
// FIXME: isCompatibleWith(uuid)
virtual bool is_byte_order_equal() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != 16) {
throw marshal_exception();
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$");
if (!std::regex_match(s.begin(), s.end(), re)) {
throw marshal_exception();
}
utils::UUID v(s);
return v.to_bytes();
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return from_value(v).get().to_sstring();
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::uuid;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return &other == this || &other == timeuuid_type.get();
}
};
struct inet_addr_type_impl : concrete_type<net::ipv4_address> {
inet_addr_type_impl() : concrete_type<net::ipv4_address>(inet_addr_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
// FIXME: support ipv6
auto& ipv4e = from_value(value);
if (ipv4e.empty()) {
return;
}
auto& ipv4 = ipv4e.get();
uint32_t u = htonl(ipv4.ip);
out = std::copy_n(reinterpret_cast<const char*>(&u), sizeof(u), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value || from_value(value).empty()) {
return 0;
}
return 4;
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
if (v.size() == 16) {
throw std::runtime_error("IPv6 addresses not supported");
}
auto ip = read_simple<int32_t>(v);
if (!v.empty()) {
throw marshal_exception();
}
return make_value(net::ipv4_address(ip));
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return less_unsigned(v1, v2);
}
virtual bool is_byte_order_equal() const override {
return true;
}
virtual bool is_byte_order_comparable() const override {
return true;
}
virtual size_t hash(bytes_view v) const override {
return std::hash<bytes_view>()(v);
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != sizeof(uint32_t)) {
throw marshal_exception();
}
}
virtual bytes from_string(sstring_view s) const override {
// FIXME: support host names
if (s.empty()) {
return bytes();
}
native_type ipv4;
try {
ipv4 = net::ipv4_address(std::string(s.data(), s.size()));
} catch (...) {
throw marshal_exception();
}
bytes b(bytes::initialized_later(), sizeof(uint32_t));
auto out = b.begin();
serialize(&ipv4, out);
return b;
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
boost::asio::ip::address_v4 ipv4(from_value(v).get().ip);
return ipv4.to_string();
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::inet;
}
};
// Integer of same length of a given type. This is useful because our
// ntoh functions only know how to operate on integers.
template <typename T> struct int_of_size;
template <typename D, typename I> 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<double> :
public int_of_size_impl<double, uint64_t> {};
template <> struct int_of_size<float> :
public int_of_size_impl<float, uint32_t> {};
template <typename T>
struct float_type_traits {
static double read_nonempty(bytes_view v) {
union {
T d;
typename int_of_size<T>::itype i;
} x;
x.i = read_simple_exactly<typename int_of_size<T>::itype>(v);
return x.d;
}
};
template<> struct simple_type_traits<float> : public float_type_traits<float> {};
template<> struct simple_type_traits<double> : public float_type_traits<double> {};
template <typename T>
struct floating_type_impl : public simple_type_impl<T> {
floating_type_impl(sstring name) : simple_type_impl<T>(std::move(name)) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
T d = this->from_value(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<T>::quiet_NaN();
}
union {
T d;
typename int_of_size<T>::itype i;
} x;
x.d = d;
auto u = net::hton(x.i);
out = std::copy_n(reinterpret_cast<const char*>(&u), sizeof(u), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
return sizeof(T);
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return this->make_empty();
}
union {
T d;
typename int_of_size<T>::itype i;
} x;
x.i = read_simple<typename int_of_size<T>::itype>(v);
if (!v.empty()) {
throw marshal_exception();
}
return this->make_value(x.d);
}
virtual int32_t compare(bytes_view v1, bytes_view v2) const override {
if (v1.empty()) {
return v2.empty() ? 0 : -1;
}
if (v2.empty()) {
return 1;
}
T a = simple_type_traits<T>::read_nonempty(v1);
T b = simple_type_traits<T>::read_nonempty(v2);
// in java world NaN == NaN and NaN is greater than anything else
if (std::isnan(a) && std::isnan(b)) {
return 0;
} else if (std::isnan(a)) {
return 1;
} else if (std::isnan(b)) {
return -1;
}
// also -0 < 0
if (std::signbit(a) && !std::signbit(b)) {
return -1;
} else if (!std::signbit(a) && std::signbit(b)) {
return 1;
}
return a == b ? 0 : a < b ? -1 : 1;
}
virtual void validate(bytes_view v) const override {
if (v.size() != 0 && v.size() != sizeof(T)) {
throw marshal_exception();
}
}
virtual bytes from_string(sstring_view s) const override {
if (s.empty()) {
return bytes();
}
try {
auto d = boost::lexical_cast<T>(s.begin(), s.size());
bytes b(bytes::initialized_later(), sizeof(T));
auto out = b.begin();
auto val = this->make_value(d);
serialize(this->get_value_ptr(val), out);
return b;
}
catch(const boost::bad_lexical_cast& e) {
throw marshal_exception(sprint("Invalid number format '%s'", s));
}
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return to_sstring(this->from_value(v));
}
};
struct double_type_impl : floating_type_impl<double> {
double_type_impl() : floating_type_impl{double_type_name} { }
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::double_;
}
};
struct float_type_impl : floating_type_impl<float> {
float_type_impl() : floating_type_impl{float_type_name} { }
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::float_;
}
};
class varint_type_impl : public concrete_type<boost::multiprecision::cpp_int> {
public:
varint_type_impl() : concrete_type{varint_type_name} { }
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto& num1 = from_value(value);
if (num1.empty()) {
return;
}
auto& num = std::move(num1).get();
boost::multiprecision::cpp_int pnum = boost::multiprecision::abs(num);
std::vector<uint8_t> b;
auto size = serialized_size(value);
b.reserve(size);
if (num.sign() < 0) {
pnum -= 1;
}
for (unsigned i = 0; i < size; i++) {
auto v = boost::multiprecision::integer_modulus(pnum, 256);
pnum >>= 8;
if (num.sign() < 0) {
v = ~v;
}
b.push_back(v);
}
std::copy(b.crbegin(), b.crend(), out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
auto& num1 = from_value(value);
if (num1.empty()) {
return 0;
}
auto&& num = num1.get();
if (!num) {
return 1;
}
auto pnum = abs(num);
return align_up(boost::multiprecision::msb(pnum) + 2, 8u) / 8;
}
virtual int32_t compare(bytes_view v1, bytes_view v2) const override {
if (v1.empty()) {
return v2.empty() ? 0 : -1;
}
if (v2.empty()) {
return 1;
}
auto a = from_value(deserialize(v1));
auto b = from_value(deserialize(v2));
return a == b ? 0 : a < b ? -1 : 1;
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return compare(v1, v2) < 0;
}
virtual size_t hash(bytes_view v) const override {
bytes b(v.begin(), v.end());
return std::hash<sstring>()(to_string(b));
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
auto negative = v.front() < 0;
boost::multiprecision::cpp_int num;
for (uint8_t b : v) {
if (negative) {
b = ~b;
}
num <<= 8;
num += b;
}
if (negative) {
num += 1;
}
return make_value(negative ? -num : num);
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return from_value(v).get().str();
}
virtual bytes from_string(sstring_view text) const override {
if (text.empty()) {
return bytes();
}
try {
std::string str(text.begin(), text.end());
native_type num(str);
bytes b(bytes::initialized_later(), serialized_size(&num));
auto out = b.begin();
serialize(&num, out);
return b;
} catch (...) {
throw marshal_exception(sprint("unable to make int from '%s'", text));
}
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::varint;
}
virtual bool is_value_compatible_with_internal(const abstract_type& other) const override {
return &other == this || int32_type->is_value_compatible_with(other) || long_type->is_value_compatible_with(other);
}
friend class decimal_type_impl;
};
class decimal_type_impl : public concrete_type<big_decimal> {
public:
decimal_type_impl() : concrete_type{decimal_type_name} { }
virtual void serialize(const void* value, bytes::iterator& out) const override {
if (!value) {
return;
}
auto bd1 = from_value(value);
if (bd1.empty()) {
return;
}
auto&& bd = std::move(bd1).get();
auto u = net::hton(bd.scale());
out = std::copy_n(reinterpret_cast<const char*>(&u), sizeof(int32_t), out);
varint_type_impl::native_type unscaled_value = bd.unscaled_value();
varint_type->serialize(&unscaled_value, out);
}
virtual size_t serialized_size(const void* value) const override {
if (!value) {
return 0;
}
auto& bd1 = from_value(value);
if (bd1.empty()) {
return 0;
}
auto&& bd = std::move(bd1).get();
varint_type_impl::native_type unscaled_value = bd.unscaled_value();
return sizeof(int32_t) + varint_type->serialized_size(&unscaled_value);
}
virtual int32_t compare(bytes_view v1, bytes_view v2) const override {
if (v1.empty()) {
return v2.empty() ? 0 : -1;
}
if (v2.empty()) {
return 1;
}
auto a = from_value(deserialize(v1));
auto b = from_value(deserialize(v2));
if (a.empty() && b.empty()) {
return 0;
}
if (a.empty() && !b.empty()) {
return -1;
}
if (!a.empty() && b.empty()) {
return 1;
}
return a.get().compare(b.get());
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return compare(v1, v2) < 0;
}
virtual size_t hash(bytes_view v) const override {
bytes b(v.begin(), v.end());
return std::hash<sstring>()(to_string(b));
}
virtual data_value deserialize(bytes_view v) const override {
if (v.empty()) {
return make_empty();
}
auto scale = read_simple<int32_t>(v);
data_value unscaled = varint_type->deserialize(v);
auto real_varint_type = static_cast<const varint_type_impl*>(varint_type.get()); // yuck
return make_value(big_decimal(scale, real_varint_type->from_value(unscaled).get()));
}
virtual sstring to_string(const bytes& b) const override {
auto v = deserialize(b);
if (v.is_null()) {
return "";
}
return from_value(v).get().to_string();
}
virtual bytes from_string(sstring_view text) const override {
if (text.empty()) {
return bytes();
}
try {
native_type bd(text);
bytes b(bytes::initialized_later(), serialized_size(&bd));
auto out = b.begin();
serialize(&bd, out);
return b;
} catch (...) {
throw marshal_exception(sprint("unable to make BigDecimal from '%s'", text));
}
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::decimal;
}
};
class counter_type_impl : public abstract_type {
public:
counter_type_impl() : abstract_type{counter_type_name} { }
virtual void serialize(const void* value, bytes::iterator& out) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual size_t serialized_size(const void* value) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual int32_t compare(bytes_view v1, bytes_view v2) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual size_t hash(bytes_view v) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual data_value deserialize(bytes_view v) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual sstring to_string(const bytes& b) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual bytes from_string(sstring_view text) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual bool is_counter() const override {
return true;
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() const override {
return cql3::cql3_type::counter;
}
virtual size_t native_value_size() const override {
fail(unimplemented::cause::COUNTERS);
}
virtual size_t native_value_alignment() const override {
fail(unimplemented::cause::COUNTERS);
}
virtual void native_value_copy(const void* from, void* to) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual void native_value_move(void* from, void* to) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual void native_value_destroy(void* object) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual void native_value_delete(void* object) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual void* native_value_clone(const void* object) const override {
fail(unimplemented::cause::COUNTERS);
}
virtual const std::type_info& native_typeid() const {
fail(unimplemented::cause::COUNTERS);
}
virtual bool references_user_type(const sstring& keyspace, const bytes& name) const override {
return false;
}
virtual std::experimental::optional<data_type> update_user_type(const shared_ptr<const user_type_impl> updated) const {
return std::experimental::nullopt;
}
};
struct empty_type_impl : abstract_type {
empty_type_impl() : abstract_type(empty_type_name) {}
virtual void serialize(const void* value, bytes::iterator& out) const override {
}
virtual size_t serialized_size(const void* value) const override {
return 0;
}
virtual bool less(bytes_view v1, bytes_view v2) const override {
return false;
}
virtual size_t hash(bytes_view v) const override {
return 0;
}
virtual data_value deserialize(bytes_view v) const override {
return data_value::make_null(shared_from_this());
}
virtual sstring to_string(const bytes& b) const override {
// FIXME:
abort();
}
virtual bytes from_string(sstring_view text) const override {
// FIXME:
abort();
}
virtual shared_ptr<cql3::cql3_type> as_cql3_type() const override {
// Can't happen
abort();
}
virtual size_t native_value_size() const override {
// Can't happen
abort();
}
virtual size_t native_value_alignment() const override {
// Can't happen
abort();
}
virtual void native_value_copy(const void* from, void* to) const override {
// Can't happen
abort();
}
virtual void native_value_move(void* from, void* to) const override {
// Can't happen
abort();
}
virtual void native_value_destroy(void* object) const override {
// Can't happen
abort();
}
virtual void native_value_delete(void* object) const override {
// Can't happen
abort();
}
virtual void* native_value_clone(const void* object) const override {
// Can't happen
abort();
}
virtual const std::type_info& native_typeid() const {
// Can't happen
abort();
}
virtual bool references_user_type(const sstring& keyspace, const bytes& name) const override {
// Can't happen
abort();
}
virtual std::experimental::optional<data_type> update_user_type(const shared_ptr<const user_type_impl> updated) const {
// Can't happen
abort();
}
};
logging::logger collection_type_impl::_logger("collection_type_impl");
const size_t collection_type_impl::max_elements;
thread_local std::unordered_map<data_type, shared_ptr<cql3::cql3_type>> collection_type_impl::_cql3_type_cache;
const collection_type_impl::kind collection_type_impl::kind::map(
[] (shared_ptr<cql3::column_specification> collection, bool is_key) -> shared_ptr<cql3::column_specification> {
return is_key ? cql3::maps::key_spec_of(*collection) : cql3::maps::value_spec_of(*collection);
});
const collection_type_impl::kind collection_type_impl::kind::set(
[] (shared_ptr<cql3::column_specification> collection, bool is_key) -> shared_ptr<cql3::column_specification> {
return cql3::sets::value_spec_of(collection);
});
const collection_type_impl::kind collection_type_impl::kind::list(
[] (shared_ptr<cql3::column_specification> collection, bool is_key) -> shared_ptr<cql3::column_specification> {
return cql3::lists::value_spec_of(collection);
});
shared_ptr<cql3::column_specification>
collection_type_impl::kind::make_collection_receiver(shared_ptr<cql3::column_specification> collection, bool is_key) const {
return _impl(std::move(collection), is_key);
}
shared_ptr<cql3::column_specification>
collection_type_impl::make_collection_receiver(shared_ptr<cql3::column_specification> collection, bool is_key) const {
return _kind.make_collection_receiver(std::move(collection), is_key);
}
std::vector<atomic_cell>
collection_type_impl::enforce_limit(std::vector<atomic_cell> cells, int version) const {
assert(is_multi_cell());
if (version >= 3 || cells.size() <= max_elements) {
return cells;
}
_logger.error("Detected collection with {} elements, more than the {} limit. Only the first {} elements will be returned to the client. "
"Please see http://cassandra.apache.org/doc/cql3/CQL.html#collections for more details.", cells.size(), max_elements, max_elements);
cells.erase(cells.begin() + max_elements, cells.end());
return cells;
}
bytes
collection_type_impl::serialize_for_native_protocol(std::vector<atomic_cell> cells, int version) const {
assert(is_multi_cell());
cells = enforce_limit(std::move(cells), version);
std::vector<bytes> values = serialized_values(std::move(cells));
// FIXME: implement
abort();
// return CollectionSerializer.pack(values, cells.size(), version);
}
bool
collection_type_impl::is_compatible_with(const abstract_type& previous) const {
if (this == &previous) {
return true;
}
if (!previous.is_collection()) {
return false;
}
auto& cprev = static_cast<const collection_type_impl&>(previous);
if (&_kind != &cprev._kind) {
return false;
}
if (is_multi_cell() != cprev.is_multi_cell()) {
return false;
}
if (!is_multi_cell()) {
return is_compatible_with_frozen(cprev);
}
if (!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 value_comparator()->is_value_compatible_with(*cprev.value_comparator());
}
bool
collection_type_impl::is_value_compatible_with_internal(const abstract_type& previous) const {
// for multi-cell collections, compatibility and value-compatibility are the same
if (is_multi_cell() || previous.is_multi_cell()) {
return is_compatible_with(previous);
}
if (!previous.is_collection()) {
return false;
}
auto& cprev = static_cast<const collection_type_impl&>(previous);
if (&_kind != &cprev._kind) {
return false;
}
return is_value_compatible_with_frozen(cprev);
}
shared_ptr<cql3::cql3_type>
collection_type_impl::as_cql3_type() const {
auto ret = _cql3_type_cache[shared_from_this()];
if (!ret) {
auto name = cql3_type_name();
if (!is_multi_cell()) {
name = "frozen<" + name + ">";
}
ret = make_shared<cql3::cql3_type>(name, shared_from_this(), false);
_cql3_type_cache[shared_from_this()] = ret;
}
return ret;
}
bytes
collection_type_impl::to_value(collection_mutation_view mut, cql_serialization_format sf) const {
return to_value(deserialize_mutation_form(mut), sf);
}
collection_type_impl::mutation
collection_type_impl::mutation_view::materialize() const {
collection_type_impl::mutation m;
m.tomb = tomb;
m.cells.reserve(cells.size());
for (auto&& e : cells) {
m.cells.emplace_back(bytes(e.first.begin(), e.first.end()), e.second);
}
return m;
}
size_t collection_size_len(cql_serialization_format sf) {
if (sf.using_32_bits_for_collections()) {
return sizeof(int32_t);
}
return sizeof(uint16_t);
}
size_t collection_value_len(cql_serialization_format sf) {
if (sf.using_32_bits_for_collections()) {
return sizeof(int32_t);
}
return sizeof(uint16_t);
}
int read_collection_size(bytes_view& in, cql_serialization_format sf) {
if (sf.using_32_bits_for_collections()) {
return read_simple<int32_t>(in);
} else {
return read_simple<uint16_t>(in);
}
}
void write_collection_size(bytes::iterator& out, int size, cql_serialization_format sf) {
if (sf.using_32_bits_for_collections()) {
serialize_int32(out, size);
} else {
serialize_int16(out, uint16_t(size));
}
}
bytes_view read_collection_value(bytes_view& in, cql_serialization_format sf) {
auto size = sf.using_32_bits_for_collections() ? read_simple<int32_t>(in) : read_simple<uint16_t>(in);
return read_simple_bytes(in, size);
}
void write_collection_value(bytes::iterator& out, cql_serialization_format sf, bytes_view val_bytes) {
if (sf.using_32_bits_for_collections()) {
serialize_int32(out, int32_t(val_bytes.size()));
} else {
serialize_int16(out, uint16_t(val_bytes.size()));
}
out = std::copy_n(val_bytes.begin(), val_bytes.size(), out);
}
void write_collection_value(bytes::iterator& out, cql_serialization_format sf, data_type type, const data_value& value) {
size_t val_len = type->serialized_size(type->get_value_ptr(value));
if (sf.using_32_bits_for_collections()) {
serialize_int32(out, val_len);
} else {
serialize_int16(out, val_len);
}
type->serialize(type->get_value_ptr(value), out);
}
map_type
map_type_impl::get_instance(data_type keys, data_type values, bool is_multi_cell) {
return intern::get_instance(std::move(keys), std::move(values), is_multi_cell);
}
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(make_map_type_name(keys, values, is_multi_cell), kind::map)
, _keys(std::move(keys))
, _values(std::move(values))
, _is_multi_cell(is_multi_cell) {
}
data_type
map_type_impl::freeze() const {
if (_is_multi_cell) {
return get_instance(_keys, _values, false);
} else {
return shared_from_this();
}
}
bool
map_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const {
assert(!_is_multi_cell);
auto* p = dynamic_cast<const map_type_impl*>(&previous);
if (!p) {
return false;
}
return _keys->is_compatible_with(*p->_keys)
&& _values->is_compatible_with(*p->_values);
}
bool
map_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const {
assert(!_is_multi_cell);
auto* p = dynamic_cast<const map_type_impl*>(&previous);
if (!p) {
return false;
}
return _keys->is_compatible_with(*p->_keys)
&& _values->is_value_compatible_with(*p->_values);
}
bool
map_type_impl::less(bytes_view o1, bytes_view o2) const {
return compare_maps(_keys, _values, o1, o2) < 0;
}
int32_t
map_type_impl::compare_maps(data_type keys, data_type values, bytes_view o1, bytes_view o2) {
if (o1.empty()) {
return o2.empty() ? 0 : -1;
} else if (o2.empty()) {
return 1;
}
auto sf = cql_serialization_format::internal();
int size1 = read_collection_size(o1, sf);
int size2 = read_collection_size(o2, sf);
// FIXME: use std::lexicographical_compare()
for (int i = 0; i < std::min(size1, size2); ++i) {
auto k1 = read_collection_value(o1, sf);
auto k2 = read_collection_value(o2, sf);
auto cmp = keys->compare(k1, k2);
if (cmp != 0) {
return cmp;
}
auto v1 = read_collection_value(o1, sf);
auto v2 = read_collection_value(o2, sf);
cmp = values->compare(v1, v2);
if (cmp != 0) {
return cmp;
}
}
return size1 == size2 ? 0 : (size1 < size2 ? -1 : 1);
}
void
map_type_impl::serialize(const void* value, bytes::iterator& out) const {
return serialize(value, out, cql_serialization_format::internal());
}
size_t
map_type_impl::serialized_size(const void* value) const {
auto& m = from_value(value);
size_t len = collection_size_len(cql_serialization_format::internal());
size_t psz = collection_value_len(cql_serialization_format::internal());
for (auto&& kv : m) {
len += psz + _keys->serialized_size(get_value_ptr(kv.first));
len += psz + _values->serialized_size(get_value_ptr(kv.second));
}
return len;
}
void
map_type_impl::serialize(const void* value, bytes::iterator& out, cql_serialization_format sf) const {
auto& m = from_value(value);
write_collection_size(out, m.size(), sf);
for (auto&& kv : m) {
write_collection_value(out, sf, _keys, kv.first);
write_collection_value(out, sf, _values, kv.second);
}
}
data_value
map_type_impl::deserialize(bytes_view v) const {
return deserialize(v, cql_serialization_format::internal());
}
data_value
map_type_impl::deserialize(bytes_view in, cql_serialization_format sf) const {
native_type m;
auto size = read_collection_size(in, sf);
for (int i = 0; i < size; ++i) {
auto kb = read_collection_value(in, sf);
auto k = _keys->deserialize(kb);
auto vb = read_collection_value(in, sf);
auto v = _values->deserialize(vb);
m.insert(m.end(), std::make_pair(std::move(k), std::move(v)));
}
return make_value(std::move(m));
}
sstring
map_type_impl::to_string(const bytes& b) const {
bool include_frozen_type = !is_multi_cell();
std::ostringstream out;
bool first = true;
auto v = bytes_view(b);
auto sf = cql_serialization_format::internal();
if (include_frozen_type) {
out << "(";
}
auto size = read_collection_size(v, sf);
for (int i = 0; i < size; ++i) {
auto kb = read_collection_value(v, sf);
auto vb = read_collection_value(v, sf);
if (first) {
first = false;
} else {
out << ", ";
}
out << "{" << _keys->to_string(bytes(kb.begin(), kb.end())) << " : ";
out << _values->to_string(bytes(vb.begin(), vb.end())) << "}";
}
if (include_frozen_type) {
out << ")";
}
return out.str();
}
size_t
map_type_impl::hash(bytes_view v) const {
// FIXME:
abort();
}
bytes
map_type_impl::from_string(sstring_view text) const {
// FIXME:
abort();
}
std::vector<bytes>
map_type_impl::serialized_values(std::vector<atomic_cell> cells) const {
// FIXME:
abort();
}
bytes
map_type_impl::to_value(mutation_view mut, cql_serialization_format sf) const {
std::vector<bytes_view> tmp;
tmp.reserve(mut.cells.size() * 2);
for (auto&& e : mut.cells) {
if (e.second.is_live(mut.tomb, false)) {
tmp.emplace_back(e.first);
tmp.emplace_back(e.second.value());
}
}
return pack(tmp.begin(), tmp.end(), tmp.size() / 2, sf);
}
bytes
map_type_impl::serialize_partially_deserialized_form(
const std::vector<std::pair<bytes_view, bytes_view>>& v, cql_serialization_format sf) {
size_t len = collection_value_len(sf) * v.size() * 2 + collection_size_len(sf);
for (auto&& e : v) {
len += e.first.size() + e.second.size();
}
bytes b(bytes::initialized_later(), len);
bytes::iterator out = b.begin();
write_collection_size(out, v.size(), sf);
for (auto&& e : v) {
write_collection_value(out, sf, e.first);
write_collection_value(out, sf, e.second);
}
return b;
}
sstring
map_type_impl::cql3_type_name() const {
return sprint("map<%s, %s>", _keys->as_cql3_type(), _values->as_cql3_type());
}
bool
map_type_impl::references_user_type(const sstring& keyspace, const bytes& name) const {
return _keys->references_user_type(keyspace, name) || _values->references_user_type(keyspace, name);
}
std::experimental::optional<data_type>
map_type_impl::update_user_type(const shared_ptr<const user_type_impl> updated) const {
auto k = _keys->update_user_type(updated);
auto v = _values->update_user_type(updated);
if (!k && !v) {
return std::experimental::nullopt;
}
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(
get_instance(k ? *k : _keys, v ? *v : _values, _is_multi_cell)));
}
auto collection_type_impl::deserialize_mutation_form(collection_mutation_view cm) -> mutation_view {
auto&& in = cm.data;
mutation_view ret;
auto has_tomb = read_simple<bool>(in);
if (has_tomb) {
auto ts = read_simple<api::timestamp_type>(in);
auto ttl = read_simple<gc_clock::duration::rep>(in);
ret.tomb = tombstone{ts, gc_clock::time_point(gc_clock::duration(ttl))};
}
auto nr = read_simple<uint32_t>(in);
ret.cells.reserve(nr);
for (uint32_t i = 0; i != nr; ++i) {
// FIXME: we could probably avoid the need for size
auto ksize = read_simple<uint32_t>(in);
auto key = read_simple_bytes(in, ksize);
auto vsize = read_simple<uint32_t>(in);
auto value = atomic_cell_view::from_bytes(read_simple_bytes(in, vsize));
ret.cells.emplace_back(key, value);
}
assert(in.empty());
return ret;
}
bool collection_type_impl::is_empty(collection_mutation_view cm) const {
auto&& in = cm.data;
auto has_tomb = read_simple<bool>(in);
return !has_tomb && read_simple<uint32_t>(in) == 0;
}
bool collection_type_impl::is_any_live(collection_mutation_view cm, tombstone tomb, gc_clock::time_point now) const {
auto&& in = cm.data;
auto has_tomb = read_simple<bool>(in);
if (has_tomb) {
auto ts = read_simple<api::timestamp_type>(in);
auto ttl = read_simple<gc_clock::duration::rep>(in);
tomb.apply(tombstone{ts, gc_clock::time_point(gc_clock::duration(ttl))});
}
auto nr = read_simple<uint32_t>(in);
for (uint32_t i = 0; i != nr; ++i) {
auto ksize = read_simple<uint32_t>(in);
in.remove_prefix(ksize);
auto vsize = read_simple<uint32_t>(in);
auto value = atomic_cell_view::from_bytes(read_simple_bytes(in, vsize));
if (value.is_live(tomb, now, false)) {
return true;
}
}
return false;
}
api::timestamp_type collection_type_impl::last_update(collection_mutation_view cm) const {
auto&& in = cm.data;
api::timestamp_type max = api::missing_timestamp;
auto has_tomb = read_simple<bool>(in);
if (has_tomb) {
max = std::max(max, read_simple<api::timestamp_type>(in));
(void)read_simple<gc_clock::duration::rep>(in);
}
auto nr = read_simple<uint32_t>(in);
for (uint32_t i = 0; i != nr; ++i) {
auto ksize = read_simple<uint32_t>(in);
in.remove_prefix(ksize);
auto vsize = read_simple<uint32_t>(in);
auto value = atomic_cell_view::from_bytes(read_simple_bytes(in, vsize));
max = std::max(value.timestamp(), max);
}
return max;
}
template <typename Iterator>
collection_mutation
do_serialize_mutation_form(
const tombstone& tomb,
boost::iterator_range<Iterator> cells) {
auto element_size = [] (size_t c, auto&& e) -> size_t {
return c + 8 + e.first.size() + e.second.serialize().size();
};
auto size = accumulate(cells, (size_t)4, element_size);
size += 1;
if (tomb) {
size += sizeof(tomb.timestamp) + sizeof(tomb.deletion_time);
}
bytes ret(bytes::initialized_later(), size);
bytes::iterator out = ret.begin();
*out++ = bool(tomb);
if (tomb) {
write(out, tomb.timestamp);
write(out, tomb.deletion_time.time_since_epoch().count());
}
auto writeb = [&out] (bytes_view v) {
serialize_int32(out, v.size());
out = std::copy_n(v.begin(), v.size(), out);
};
// FIXME: overflow?
serialize_int32(out, boost::distance(cells));
for (auto&& kv : cells) {
auto&& k = kv.first;
auto&& v = kv.second;
writeb(k);
writeb(v.serialize());
}
return collection_mutation{std::move(ret)};
}
bool collection_type_impl::mutation::compact_and_expire(tombstone base_tomb, gc_clock::time_point query_time,
can_gc_fn& can_gc, gc_clock::time_point gc_before)
{
bool any_live = false;
tomb.apply(base_tomb);
std::vector<std::pair<bytes, atomic_cell>> survivors;
for (auto&& name_and_cell : cells) {
atomic_cell& cell = name_and_cell.second;
auto cannot_erase_cell = [&] {
return cell.deletion_time() >= gc_before || !can_gc(tombstone(cell.timestamp(), cell.deletion_time()));
};
if (cell.is_covered_by(tomb, false)) {
continue;
}
if (cell.has_expired(query_time)) {
if (cannot_erase_cell()) {
survivors.emplace_back(std::make_pair(
std::move(name_and_cell.first), atomic_cell::make_dead(cell.timestamp(), cell.deletion_time())));
}
} else if (!cell.is_live()) {
if (cannot_erase_cell()) {
survivors.emplace_back(std::move(name_and_cell));
}
} else {
any_live |= true;
survivors.emplace_back(std::move(name_and_cell));
}
}
cells = std::move(survivors);
if (tomb.deletion_time < gc_before && can_gc(tomb)) {
tomb = tombstone();
}
return any_live;
}
collection_mutation
collection_type_impl::serialize_mutation_form(const mutation& mut) {
return do_serialize_mutation_form(mut.tomb, boost::make_iterator_range(mut.cells.begin(), mut.cells.end()));
}
collection_mutation
collection_type_impl::serialize_mutation_form(mutation_view mut) {
return do_serialize_mutation_form(mut.tomb, boost::make_iterator_range(mut.cells.begin(), mut.cells.end()));
}
collection_mutation
collection_type_impl::serialize_mutation_form_only_live(mutation_view mut, gc_clock::time_point now) {
return do_serialize_mutation_form(mut.tomb, mut.cells | boost::adaptors::filtered([t = mut.tomb, now] (auto&& e) {
return e.second.is_live(t, now, false);
}));
}
collection_mutation
collection_type_impl::merge(collection_mutation_view a, collection_mutation_view b) const {
auto aa = deserialize_mutation_form(a);
auto bb = deserialize_mutation_form(b);
mutation_view merged;
merged.cells.reserve(aa.cells.size() + bb.cells.size());
using element_type = std::pair<bytes_view, atomic_cell_view>;
auto key_type = name_comparator();
auto compare = [key_type] (const element_type& e1, const element_type& e2) {
return key_type->less(e1.first, e2.first);
};
auto merge = [this] (const element_type& e1, const element_type& e2) {
// FIXME: use std::max()?
return std::make_pair(e1.first, compare_atomic_cell_for_merge(e1.second, e2.second) > 0 ? e1.second : e2.second);
};
// applied to a tombstone, returns a predicate checking whether a cell is killed by
// the tombstone
auto cell_killed = [] (const std::experimental::optional<tombstone>& t) {
return [&t] (const element_type& e) {
if (!t) {
return false;
}
// tombstone wins if timestamps equal here, unlike row tombstones
if (t->timestamp < e.second.timestamp()) {
return false;
}
return true;
// FIXME: should we consider TTLs too?
};
};
combine(aa.cells.begin(), std::remove_if(aa.cells.begin(), aa.cells.end(), cell_killed(bb.tomb)),
bb.cells.begin(), std::remove_if(bb.cells.begin(), bb.cells.end(), cell_killed(aa.tomb)),
std::back_inserter(merged.cells),
compare,
merge);
merged.tomb = std::max(aa.tomb, bb.tomb);
return serialize_mutation_form(merged);
}
collection_mutation
collection_type_impl::difference(collection_mutation_view a, collection_mutation_view b) const
{
auto aa = deserialize_mutation_form(a);
auto bb = deserialize_mutation_form(b);
mutation_view diff;
diff.cells.reserve(std::max(aa.cells.size(), bb.cells.size()));
auto key_type = name_comparator();
auto it = bb.cells.begin();
for (auto&& c : aa.cells) {
while (it != bb.cells.end() && key_type->less(it->first, c.first)) {
++it;
}
if (it == bb.cells.end() || !key_type->equal(it->first, c.first)
|| compare_atomic_cell_for_merge(c.second, it->second) > 0) {
auto cell = std::make_pair(c.first, c.second);
diff.cells.emplace_back(std::move(cell));
}
}
if (aa.tomb > bb.tomb) {
diff.tomb = aa.tomb;
}
return serialize_mutation_form(diff);
}
bytes_opt
collection_type_impl::reserialize(cql_serialization_format from, cql_serialization_format to, bytes_view_opt v) const {
if (!v) {
return std::experimental::nullopt;
}
auto val = deserialize(*v, from);
bytes ret(bytes::initialized_later(), serialized_size(get_value_ptr(val))); // FIXME: serialized_size want @to
auto out = ret.begin();
serialize(get_value_ptr(val), out, to);
return ret;
}
// iterator that takes a set or list in serialized form, and emits
// each element, still in serialized form
class listlike_partial_deserializing_iterator
: public std::iterator<std::input_iterator_tag, bytes_view> {
bytes_view* _in;
int _remain;
bytes_view _cur;
cql_serialization_format _sf;
private:
struct end_tag {};
listlike_partial_deserializing_iterator(bytes_view& in, cql_serialization_format sf)
: _in(&in), _sf(sf) {
_remain = read_collection_size(*_in, _sf);
parse();
}
listlike_partial_deserializing_iterator(end_tag)
: _remain(0), _sf(cql_serialization_format::internal()) { // _sf is bogus, but doesn't matter
}
public:
bytes_view operator*() const { return _cur; }
listlike_partial_deserializing_iterator& operator++() {
--_remain;
parse();
return *this;
}
void operator++(int) {
--_remain;
parse();
}
bool operator==(const listlike_partial_deserializing_iterator& x) const {
return _remain == x._remain;
}
bool operator!=(const listlike_partial_deserializing_iterator& x) const {
return _remain != x._remain;
}
static listlike_partial_deserializing_iterator begin(bytes_view& in, cql_serialization_format sf) {
return { in, sf };
}
static listlike_partial_deserializing_iterator end(bytes_view in, cql_serialization_format sf) {
return { end_tag() };
}
private:
void parse() {
if (_remain) {
_cur = read_collection_value(*_in, _sf);
} else {
_cur = {};
}
}
};
set_type
set_type_impl::get_instance(data_type elements, bool is_multi_cell) {
return intern::get_instance(elements, is_multi_cell);
}
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(make_set_type_name(elements, is_multi_cell), kind::set)
, _elements(std::move(elements))
, _is_multi_cell(is_multi_cell) {
}
data_type
set_type_impl::value_comparator() const {
return empty_type;
}
data_type
set_type_impl::freeze() const {
if (_is_multi_cell) {
return get_instance(_elements, false);
} else {
return shared_from_this();
}
}
bool
set_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const {
assert(!_is_multi_cell);
auto* p = dynamic_cast<const set_type_impl*>(&previous);
if (!p) {
return false;
}
return _elements->is_compatible_with(*p->_elements);
}
bool
set_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const {
return is_compatible_with(previous);
}
bool
set_type_impl::less(bytes_view o1, bytes_view o2) const {
using llpdi = listlike_partial_deserializing_iterator;
auto sf = cql_serialization_format::internal();
return std::lexicographical_compare(
llpdi::begin(o1, sf), llpdi::end(o1, sf),
llpdi::begin(o2, sf), llpdi::end(o2, sf),
[this] (bytes_view o1, bytes_view o2) { return _elements->less(o1, o2); });
}
void
set_type_impl::serialize(const void* value, bytes::iterator& out) const {
return serialize(value, out, cql_serialization_format::internal());
}
size_t
set_type_impl::serialized_size(const void* value) const {
auto& s = from_value(value);
size_t len = collection_size_len(cql_serialization_format::internal());
size_t psz = collection_value_len(cql_serialization_format::internal());
for (auto&& e : s) {
len += psz + _elements->serialized_size(_elements->get_value_ptr(e));
}
return len;
}
void
set_type_impl::serialize(const void* value, bytes::iterator& out, cql_serialization_format sf) const {
auto& s = from_value(value);
write_collection_size(out, s.size(), sf);
for (auto&& e : s) {
write_collection_value(out, sf, _elements, e);
}
}
data_value
set_type_impl::deserialize(bytes_view in) const {
return deserialize(in, cql_serialization_format::internal());
}
data_value
set_type_impl::deserialize(bytes_view in, cql_serialization_format sf) const {
auto nr = read_collection_size(in, sf);
native_type s;
s.reserve(nr);
for (int i = 0; i != nr; ++i) {
auto e = _elements->deserialize(read_collection_value(in, sf));
if (e.is_null()) {
throw marshal_exception();
}
s.push_back(std::move(e));
}
return make_value(std::move(s));
}
sstring
set_type_impl::to_string(const bytes& b) const {
using llpdi = listlike_partial_deserializing_iterator;
std::ostringstream out;
bool first = true;
auto v = bytes_view(b);
auto sf = cql_serialization_format::internal();
std::for_each(llpdi::begin(v, sf), llpdi::end(v, sf), [&first, &out, this] (bytes_view e) {
if (first) {
first = false;
} else {
out << "; ";
}
out << _elements->to_string(bytes(e.begin(), e.end()));
});
return out.str();
}
size_t
set_type_impl::hash(bytes_view v) const {
// FIXME:
abort();
}
bytes
set_type_impl::from_string(sstring_view text) const {
// FIXME:
abort();
}
std::vector<bytes>
set_type_impl::serialized_values(std::vector<atomic_cell> cells) const {
// FIXME:
abort();
}
bytes
set_type_impl::to_value(mutation_view mut, cql_serialization_format sf) const {
std::vector<bytes_view> tmp;
tmp.reserve(mut.cells.size());
for (auto&& e : mut.cells) {
if (e.second.is_live(mut.tomb, false)) {
tmp.emplace_back(e.first);
}
}
return pack(tmp.begin(), tmp.end(), tmp.size(), sf);
}
bytes
set_type_impl::serialize_partially_deserialized_form(
const std::vector<bytes_view>& v, cql_serialization_format sf) const {
return pack(v.begin(), v.end(), v.size(), sf);
}
sstring
set_type_impl::cql3_type_name() const {
return sprint("set<%s>", _elements->as_cql3_type());
}
bool
set_type_impl::references_user_type(const sstring& keyspace, const bytes& name) const {
return _elements->references_user_type(keyspace, name);
}
std::experimental::optional<data_type>
set_type_impl::update_user_type(const shared_ptr<const user_type_impl> updated) const {
auto e = _elements->update_user_type(updated);
if (e) {
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(
get_instance(std::move(*e), _is_multi_cell)));
}
return std::experimental::nullopt;
}
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(make_list_type_name(elements, is_multi_cell), kind::list)
, _elements(std::move(elements))
, _is_multi_cell(is_multi_cell) {
}
data_type
list_type_impl::name_comparator() const {
return timeuuid_type;
}
data_type
list_type_impl::value_comparator() const {
return _elements;
}
data_type
list_type_impl::freeze() const {
if (_is_multi_cell) {
return get_instance(_elements, false);
} else {
return shared_from_this();
}
}
bool
list_type_impl::is_compatible_with_frozen(const collection_type_impl& previous) const {
assert(!_is_multi_cell);
auto* p = dynamic_cast<const list_type_impl*>(&previous);
if (!p) {
return false;
}
return _elements->is_compatible_with(*p->_elements);
}
bool
list_type_impl::is_value_compatible_with_frozen(const collection_type_impl& previous) const {
auto& lp = dynamic_cast<const list_type_impl&>(previous);
return _elements->is_value_compatible_with_internal(*lp._elements);
}
bool
list_type_impl::less(bytes_view o1, bytes_view o2) const {
using llpdi = listlike_partial_deserializing_iterator;
auto sf = cql_serialization_format::internal();
return std::lexicographical_compare(
llpdi::begin(o1, sf), llpdi::end(o1, sf),
llpdi::begin(o2, sf), llpdi::end(o2, sf),
[this] (bytes_view o1, bytes_view o2) { return _elements->less(o1, o2); });
}
void
list_type_impl::serialize(const void* value, bytes::iterator& out) const {
return serialize(value, out, cql_serialization_format::internal());
}
void
list_type_impl::serialize(const void* value, bytes::iterator& out, cql_serialization_format sf) const {
auto& s = from_value(value);
write_collection_size(out, s.size(), sf);
for (auto&& e : s) {
write_collection_value(out, sf, _elements, e);
}
}
size_t
list_type_impl::serialized_size(const void* value) const {
auto& s = from_value(value);
size_t len = collection_size_len(cql_serialization_format::internal());
size_t psz = collection_value_len(cql_serialization_format::internal());
for (auto&& e : s) {
len += psz + _elements->serialized_size(_elements->get_value_ptr(e));
}
return len;
}
data_value
list_type_impl::deserialize(bytes_view in) const {
return deserialize(in, cql_serialization_format::internal());
}
data_value
list_type_impl::deserialize(bytes_view in, cql_serialization_format sf) const {
auto nr = read_collection_size(in, sf);
native_type s;
s.reserve(nr);
for (int i = 0; i != nr; ++i) {
auto e = _elements->deserialize(read_collection_value(in, sf));
if (e.is_null()) {
throw marshal_exception();
}
s.push_back(std::move(e));
}
return make_value(std::move(s));
}
sstring
list_type_impl::to_string(const bytes& b) const {
using llpdi = listlike_partial_deserializing_iterator;
std::ostringstream out;
bool first = true;
auto v = bytes_view(b);
auto sf = cql_serialization_format::internal();
std::for_each(llpdi::begin(v, sf), llpdi::end(v, sf), [&first, &out, this] (bytes_view e) {
if (first) {
first = false;
} else {
out << ", ";
}
out << _elements->to_string(bytes(e.begin(), e.end()));
});
return out.str();
}
size_t
list_type_impl::hash(bytes_view v) const {
// FIXME:
abort();
}
bytes
list_type_impl::from_string(sstring_view text) const {
// FIXME:
abort();
}
std::vector<bytes>
list_type_impl::serialized_values(std::vector<atomic_cell> cells) const {
// FIXME:
abort();
}
bytes
list_type_impl::to_value(mutation_view mut, cql_serialization_format sf) const {
std::vector<bytes_view> tmp;
tmp.reserve(mut.cells.size());
for (auto&& e : mut.cells) {
if (e.second.is_live(mut.tomb, false)) {
tmp.emplace_back(e.second.value());
}
}
return pack(tmp.begin(), tmp.end(), tmp.size(), sf);
}
sstring
list_type_impl::cql3_type_name() const {
return sprint("list<%s>", _elements->as_cql3_type());
}
bool
list_type_impl::references_user_type(const sstring& keyspace, const bytes& name) const {
return _elements->references_user_type(keyspace, name);
}
std::experimental::optional<data_type>
list_type_impl::update_user_type(const shared_ptr<const user_type_impl> updated) const {
auto e = _elements->update_user_type(updated);
if (e) {
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(
get_instance(std::move(*e), _is_multi_cell)));
}
return std::experimental::nullopt;
}
tuple_type_impl::tuple_type_impl(sstring name, std::vector<data_type> types)
: concrete_type(std::move(name)), _types(std::move(types)) {
for (auto& t : _types) {
t = t->freeze();
}
}
tuple_type_impl::tuple_type_impl(std::vector<data_type> types)
: concrete_type(make_name(types)), _types(std::move(types)) {
for (auto& t : _types) {
t = t->freeze();
}
}
shared_ptr<tuple_type_impl>
tuple_type_impl::get_instance(std::vector<data_type> types) {
return ::make_shared<tuple_type_impl>(std::move(types));
}
int32_t
tuple_type_impl::compare(bytes_view v1, bytes_view v2) const {
return lexicographical_tri_compare(_types.begin(), _types.end(),
tuple_deserializing_iterator::start(v1), tuple_deserializing_iterator::finish(v1),
tuple_deserializing_iterator::start(v2), tuple_deserializing_iterator::finish(v2),
tri_compare_opt);
}
bool
tuple_type_impl::less(bytes_view v1, bytes_view v2) const {
return tuple_type_impl::compare(v1, v2) < 0;
}
size_t
tuple_type_impl::serialized_size(const void* value) const {
size_t size = 0;
if (!value) {
return size;
}
auto& v = from_value(value);
auto find_serialized_size = [] (auto&& t_v) {
const data_type& t = boost::get<0>(t_v);
const data_value& v = boost::get<1>(t_v);
if (!v.is_null() && t != v.type()) {
throw std::runtime_error("tuple element type mismatch");
}
return 4 + (v.is_null() ? 0 : t->serialized_size(t->get_value_ptr(v)));
};
return boost::accumulate(boost::combine(_types, v) | boost::adaptors::transformed(find_serialized_size), 0);
}
void
tuple_type_impl::serialize(const void* value, bytes::iterator& out) const {
if (!value) {
return;
}
auto& v = from_value(value);
auto do_serialize = [&out] (auto&& t_v) {
const data_type& t = boost::get<0>(t_v);
const data_value& v = boost::get<1>(t_v);
if (!v.is_null() && t != v.type()) {
throw std::runtime_error("tuple element type mismatch");
}
if (v.is_null()) {
write(out, int32_t(-1));
} else {
write(out, int32_t(t->serialized_size(t->get_value_ptr(v))));
t->serialize(t->get_value_ptr(v), out);
}
};
boost::range::for_each(boost::combine(_types, v), do_serialize);
}
data_value
tuple_type_impl::deserialize(bytes_view v) const {
native_type ret;
ret.reserve(_types.size());
auto ti = _types.begin();
auto vi = tuple_deserializing_iterator::start(v);
while (ti != _types.end() && vi != tuple_deserializing_iterator::finish(v)) {
data_value obj = data_value::make_null(*ti);
if (*vi) {
obj = (*ti)->deserialize(**vi);
}
ret.push_back(std::move(obj));
++ti;
++vi;
}
while (ti != _types.end()) {
ret.push_back(data_value::make_null(*ti++));
}
return make_value(std::move(ret));
}
std::vector<bytes_view_opt>
tuple_type_impl::split(bytes_view v) const {
return { tuple_deserializing_iterator::start(v), tuple_deserializing_iterator::finish(v) };
}
// Count number of ':' which are not preceded by '\'.
static std::size_t count_segments(sstring_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<sstring_view> split_field_strings(sstring_view v) {
if (v.empty()) {
return std::vector<sstring_view>();
}
std::vector<sstring_view> 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 std::move(result);
}
// Replace "\:" with ":" and "\@" with "@".
static std::string unescape(sstring_view s) {
static thread_local std::regex escaped_colon_re("\\\\:");
static thread_local std::regex escaped_at_re("\\\\@");
std::string result = s.to_string();
result = std::regex_replace(result, escaped_colon_re, ":");
result = std::regex_replace(result, escaped_at_re, "@");
return std::move(result);
}
// Concat list of bytes into a single bytes.
static bytes concat_fields(const std::vector<bytes>& fields, const std::vector<int32_t> 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<const int8_t*>(&tmp), sizeof(tmp), it);
if (field_len[i] > 0) {
it = std::copy(std::begin(fields[i]), std::end(fields[i]), it);
}
}
return std::move(result);
}
bytes
tuple_type_impl::from_string(sstring_view s) const {
std::vector<sstring_view> field_strings = split_field_strings(s);
if (field_strings.size() > size()) {
throw marshal_exception(sprint("Invalid tuple literal: too many elements. Type %s expects %d but got %d", as_cql3_type(), size(), field_strings.size()));
}
std::vector<bytes> fields(field_strings.size());
std::vector<int32_t> 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] = type(i)->from_string(field_string);
field_len[i] = fields[i].size();
}
}
return std::move(concat_fields(fields, field_len));
}
sstring
tuple_type_impl::to_string(const bytes& b) const {
throw std::runtime_error(sprint("%s not implemented", __PRETTY_FUNCTION__));
}
bool
tuple_type_impl::equals(const abstract_type& other) const {
auto x = dynamic_cast<const tuple_type_impl*>(&other);
return x && std::equal(_types.begin(), _types.end(), x->_types.begin(), x->_types.end(),
[] (auto&& a, auto&& b) { return a->equals(b); });
}
bool
tuple_type_impl::is_compatible_with(const abstract_type& previous) const {
return check_compatibility(previous, &abstract_type::is_compatible_with);
}
bool
tuple_type_impl::is_value_compatible_with_internal(const abstract_type& previous) const {
return check_compatibility(previous, &abstract_type::is_value_compatible_with);
}
bool
tuple_type_impl::check_compatibility(const abstract_type& previous, bool (abstract_type::*predicate)(const abstract_type&) const) const {
auto* x = dynamic_cast<const tuple_type_impl*>(&previous);
if (!x) {
return false;
}
auto c = std::mismatch(
_types.begin(), _types.end(),
x->_types.begin(), x->_types.end(),
[predicate] (data_type a, data_type b) { return ((*a).*predicate)(*b); });
return c.second == x->_types.end(); // this allowed to be longer
}
size_t
tuple_type_impl::hash(bytes_view v) const {
auto apply_hash = [] (auto&& type_value) {
auto&& type = boost::get<0>(type_value);
auto&& value = boost::get<1>(type_value);
return value ? type->hash(*value) : 0;
};
// FIXME: better accumulation function
return boost::accumulate(combine(_types, make_range(v))
| boost::adaptors::transformed(apply_hash),
0,
std::bit_xor<>());
}
shared_ptr<cql3::cql3_type>
tuple_type_impl::as_cql3_type() const {
return cql3::make_cql3_tuple_type(static_pointer_cast<const tuple_type_impl>(shared_from_this()));
}
sstring
tuple_type_impl::make_name(const std::vector<data_type>& types) {
return sprint("org.apache.cassandra.db.marshal.TupleType(%s)", ::join(", ", types | boost::adaptors::transformed(std::mem_fn(&abstract_type::name))));
}
bool
tuple_type_impl::references_user_type(const sstring& keyspace, const bytes& name) const {
return std::any_of(_types.begin(), _types.end(), [&](auto&& dt) { return dt->references_user_type(keyspace, name); });
}
static std::experimental::optional<std::vector<data_type>>
update_types(const std::vector<data_type> types, const user_type updated) {
std::experimental::optional<std::vector<data_type>> new_types = std::experimental::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;
}
std::experimental::optional<data_type>
tuple_type_impl::update_user_type(const shared_ptr<const user_type_impl> updated) const {
auto new_types = update_types(_types, updated);
if (new_types) {
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(
get_instance(std::move(*new_types))));
}
return std::experimental::nullopt;
}
sstring
user_type_impl::get_name_as_string() const {
auto real_utf8_type = static_cast<const utf8_type_impl*>(utf8_type.get());
return real_utf8_type->from_value(utf8_type->deserialize(_name));
}
shared_ptr<cql3::cql3_type>
user_type_impl::as_cql3_type() const {
return make_shared<cql3::cql3_type>(get_name_as_string(), shared_from_this(), false);
}
sstring
user_type_impl::make_name(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types) {
std::ostringstream os;
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 << ")";
return os.str();
}
bool
user_type_impl::equals(const abstract_type& other) const {
auto x = dynamic_cast<const user_type_impl*>(&other);
return x
&& _keyspace == x->_keyspace
&& _name == x->_name
&& std::equal(_field_names.begin(), _field_names.end(), x->_field_names.begin(), x->_field_names.end())
&& tuple_type_impl::equals(other);
}
bool
user_type_impl::references_user_type(const sstring& keyspace, const bytes& name) const {
return (_keyspace == keyspace && _name == name)
|| tuple_type_impl::references_user_type(keyspace, name);
}
std::experimental::optional<data_type>
user_type_impl::update_user_type(const shared_ptr<const user_type_impl> updated) const {
if (_keyspace == updated->_keyspace && _name == updated->_name) {
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(updated));
}
auto new_types = update_types(_types, updated);
if (new_types) {
return std::experimental::make_optional(static_pointer_cast<const abstract_type>(
get_instance(_keyspace, _name, _field_names, *new_types)));
}
return std::experimental::nullopt;
}
size_t
reversed_type_impl::native_value_size() const {
return _underlying_type->native_value_size();
}
size_t
reversed_type_impl::native_value_alignment() const {
return _underlying_type->native_value_alignment();
}
void
reversed_type_impl::native_value_copy(const void* from, void* to) const {
return _underlying_type->native_value_copy(from, to);
}
void
reversed_type_impl::native_value_move(void* from, void* to) const {
return _underlying_type->native_value_move(from, to);
}
void
reversed_type_impl::native_value_destroy(void* object) const {
return _underlying_type->native_value_destroy(object);
}
void*
reversed_type_impl::native_value_clone(const void* object) const {
return _underlying_type->native_value_clone(object);
}
void
reversed_type_impl::native_value_delete(void* object) const {
return _underlying_type->native_value_delete(object);
}
const std::type_info&
reversed_type_impl::native_typeid() const {
return _underlying_type->native_typeid();
}
thread_local const shared_ptr<const abstract_type> byte_type(make_shared<byte_type_impl>());
thread_local const shared_ptr<const abstract_type> short_type(make_shared<short_type_impl>());
thread_local const shared_ptr<const abstract_type> int32_type(make_shared<int32_type_impl>());
thread_local const shared_ptr<const abstract_type> long_type(make_shared<long_type_impl>());
thread_local const shared_ptr<const abstract_type> ascii_type(make_shared<ascii_type_impl>());
thread_local const shared_ptr<const abstract_type> bytes_type(make_shared<bytes_type_impl>());
thread_local const shared_ptr<const abstract_type> utf8_type(make_shared<utf8_type_impl>());
thread_local const shared_ptr<const abstract_type> boolean_type(make_shared<boolean_type_impl>());
thread_local const shared_ptr<const abstract_type> date_type(make_shared<date_type_impl>());
thread_local const shared_ptr<const abstract_type> timeuuid_type(make_shared<timeuuid_type_impl>());
thread_local const shared_ptr<const abstract_type> timestamp_type(make_shared<timestamp_type_impl>());
thread_local const shared_ptr<const abstract_type> simple_date_type(make_shared<simple_date_type_impl>());
thread_local const shared_ptr<const abstract_type> time_type(make_shared<time_type_impl>());
thread_local const shared_ptr<const abstract_type> uuid_type(make_shared<uuid_type_impl>());
thread_local const shared_ptr<const abstract_type> inet_addr_type(make_shared<inet_addr_type_impl>());
thread_local const shared_ptr<const abstract_type> float_type(make_shared<float_type_impl>());
thread_local const shared_ptr<const abstract_type> double_type(make_shared<double_type_impl>());
thread_local const shared_ptr<const abstract_type> varint_type(make_shared<varint_type_impl>());
thread_local const shared_ptr<const abstract_type> decimal_type(make_shared<decimal_type_impl>());
thread_local const shared_ptr<const abstract_type> counter_type(make_shared<counter_type_impl>());
thread_local const data_type empty_type(make_shared<empty_type_impl>());
data_type abstract_type::parse_type(const sstring& name)
{
static thread_local const std::unordered_map<sstring, data_type> 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 },
{ empty_type_name, empty_type },
};
auto it = types.find(name);
if (it == types.end()) {
throw std::invalid_argument(sprint("unknown type: %s\n", name));
}
return it->second;
}
data_value::~data_value() {
if (_value) {
_type->native_value_delete(_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, v)) {
}
data_value::data_value(const char* v) : data_value(make_new(utf8_type, sstring(v))) {
}
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(net::ipv4_address v) : data_value(make_new(inet_addr_type, v)) {
}
data_value::data_value(db_clock::time_point v) : data_value(make_new(date_type, v)) {
}
data_value::data_value(boost::multiprecision::cpp_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
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_user_value(data_type type, user_type_impl::native_type value) {
return data_value::make_new(std::move(type), std::move(value));
}
bytes
date_type_impl::from_string(sstring_view s) const {
native_type n = db_clock::time_point(db_clock::duration(timestamp_type_impl::timestamp_from_string(s)));
bytes ret(bytes::initialized_later(), serialized_size(&n));
auto iter = ret.begin();
serialize(&n, iter);
return ret;
}
std::ostream& operator<<(std::ostream& out, const data_value& v) {
if (v.is_null()) {
return out << "null";
}
bytes b(bytes::initialized_later(), v.serialized_size());
auto i = b.begin();
v.serialize(i);
return out << v.type()->to_string(b);
}