diff --git a/configure.py b/configure.py index 83c03aa35a..964c4a9a01 100755 --- a/configure.py +++ b/configure.py @@ -353,7 +353,7 @@ boost_test_lib = [ ] defines = [] -libs = '-laio -lboost_program_options -lboost_system -lboost_filesystem -lstdc++ -lm -lboost_unit_test_framework -lboost_thread -lcryptopp -lrt -lyaml-cpp' +libs = '-laio -lboost_program_options -lboost_system -lboost_filesystem -lstdc++ -lm -lboost_unit_test_framework -lboost_thread -lcryptopp -lrt -lyaml-cpp -lboost_date_time' hwloc_libs = '-lhwloc -lnuma -lpciaccess -lxml2 -lz' urchin_libs = '-llz4 -lsnappy -lz' diff --git a/cql3/Cql.g b/cql3/Cql.g index 0c2635e1d9..59ae03c9ee 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -1327,9 +1327,7 @@ native_type returns [shared_ptr t] #endif | K_DOUBLE { $t = cql3_type::double_; } | K_FLOAT { $t = cql3_type::float_; } -#if 0 - | K_INET { $t = CQL3Type.Native.INET;} -#endif + | K_INET { $t = cql3_type::inet; } | K_INT { $t = cql3_type::int_; } | K_TEXT { $t = cql3_type::text; } | K_TIMESTAMP { $t = cql3_type::timestamp; } diff --git a/cql3/attributes.hh b/cql3/attributes.hh index c7be67ac78..ea8ecaaffb 100644 --- a/cql3/attributes.hh +++ b/cql3/attributes.hh @@ -75,7 +75,7 @@ public: try { data_type_for()->validate(*tval); - } catch (exceptions::marshal_exception e) { + } catch (marshal_exception e) { throw exceptions::invalid_request_exception("Invalid timestamp value"); } return boost::any_cast(data_type_for()->deserialize(*tval)); @@ -93,7 +93,7 @@ public: try { data_type_for()->validate(*tval); } - catch (exceptions::marshal_exception e) { + catch (marshal_exception e) { throw exceptions::invalid_request_exception("Invalid TTL value"); } diff --git a/cql3/constants.cc b/cql3/constants.cc index ddf4abffd6..b962087118 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -57,7 +57,7 @@ constants::literal::parsed_value(data_type validator) return long_type->from_string(_text); } return validator->from_string(_text); - } catch (const exceptions::marshal_exception& e) { + } catch (const marshal_exception& e) { throw exceptions::invalid_request_exception(e.what()); } } diff --git a/cql3/constants.hh b/cql3/constants.hh index 0078fc94ef..839fe73cb3 100644 --- a/cql3/constants.hh +++ b/cql3/constants.hh @@ -153,7 +153,7 @@ public: _receiver->type->validate(value.value()); } return value; - } catch (const exceptions::marshal_exception& e) { + } catch (const marshal_exception& e) { throw exceptions::invalid_request_exception(e.what()); } } diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index f32dbb363d..0e11b033aa 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -32,27 +32,6 @@ namespace exceptions { -class invalid_request_exception : public std::logic_error { -public: - invalid_request_exception(std::string cause) - : logic_error(cause) - { } -}; - -class keyspace_not_defined_exception : public invalid_request_exception { -public: - keyspace_not_defined_exception(std::string cause) - : invalid_request_exception(cause) - { } -}; - -class marshal_exception : public std::logic_error { -public: - marshal_exception(std::string cause) - : logic_error(cause) - { } -}; - enum class exception_code : int32_t { SERVER_ERROR = 0x0000, PROTOCOL_ERROR = 0x000A, @@ -101,6 +80,20 @@ public: using cassandra_exception::cassandra_exception; }; +class invalid_request_exception : public request_validation_exception { +public: + invalid_request_exception(sstring cause) + : request_validation_exception(exception_code::INVALID, cause) + { } +}; + +class keyspace_not_defined_exception : public invalid_request_exception { +public: + keyspace_not_defined_exception(std::string cause) + : invalid_request_exception(cause) + { } +}; + class prepared_query_not_found_exception : public request_validation_exception { public: prepared_query_not_found_exception(bytes id) diff --git a/tests/urchin/cql_query_test.cc b/tests/urchin/cql_query_test.cc index 6ee5f7a7a1..a9981fd133 100644 --- a/tests/urchin/cql_query_test.cc +++ b/tests/urchin/cql_query_test.cc @@ -998,3 +998,67 @@ SEASTAR_TEST_CASE(test_ttl) { }); } +SEASTAR_TEST_CASE(test_types) { + return do_with_cql_env([] (cql_test_env& e) { + return make_ready_future<>().then([&e] { + return e.execute_cql( + "CREATE TABLE all_types (" + " a ascii PRIMARY KEY," + " b bigint," + " c blob," + " d boolean," + " e double," + " f float," + " g inet," + " h int," + " i text," + " j timestamp," + " k timeuuid," + " l uuid," + " m varchar," + ");").discard_result(); + }).then([&e] { + e.require_table_exists("ks", "all_types"); + return e.execute_cql( + "INSERT INTO all_types (a, b, c, d, e, f, g, h, i, j, k, l, m) VALUES (" + " 'ascii'," + " 123456789," + " 0xdeadbeef," + " true," + " 3.14," + " 3.14," + " '127.0.0.1'," + " 3," + " 'zażółć gęślą jaźń'," + " '2001-10-18 14:15:55.134+0000'," + " d2177dd0-eaa2-11de-a572-001b779c76e3," + " d2177dd0-eaa2-11de-a572-001b779c76e3," + " 'varchar'" + ");").discard_result(); + }).then([&e] { + return e.execute_cql("SELECT * FROM all_types WHERE a = 'ascii'"); + }).then([&e] (auto msg) { + struct tm t = { 0 }; + t.tm_year = 2001 - 1900; + t.tm_mon = 10 - 1; + t.tm_mday = 18; + t.tm_hour = 14; + t.tm_min = 15; + t.tm_sec = 55; + auto tp = db_clock::from_time_t(timegm(&t)) + std::chrono::milliseconds(134); + assert_that(msg).is_rows().with_rows({ + { + ascii_type->decompose(sstring("ascii")), long_type->decompose(123456789l), + from_hex("deadbeef"), boolean_type->decompose(true), + double_type->decompose(3.14), float_type->decompose(3.14f), + inet_addr_type->decompose(net::ipv4_address("127.0.0.1")), + int32_type->decompose(3), utf8_type->decompose(sstring("zażółć gęślą jaźń")), + timestamp_type->decompose(tp), + timeuuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), + uuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), + utf8_type->decompose(sstring("varchar")) + } + }); + }); + }); +} diff --git a/tests/urchin/types_test.cc b/tests/urchin/types_test.cc index d83d153d53..b1c1e59d76 100644 --- a/tests/urchin/types_test.cc +++ b/tests/urchin/types_test.cc @@ -6,9 +6,24 @@ #define BOOST_TEST_MODULE core #include +#include +#include +#include #include "types.hh" #include "compound.hh" +using namespace std::literals::chrono_literals; + +void test_parsing_fails(const shared_ptr& type, sstring str) +{ + try { + type->from_string(str); + BOOST_FAIL(sprint("Parsing of '%s' should have failed", str)); + } catch (const marshal_exception& e) { + // expected + } +} + BOOST_AUTO_TEST_CASE(test_bytes_type_string_conversions) { BOOST_REQUIRE(bytes_type->equal(bytes_type->from_string("616263646566"), bytes_type->decompose(bytes{"abcdef"}))); } @@ -35,23 +50,104 @@ BOOST_AUTO_TEST_CASE(test_int32_type_string_conversions) { BOOST_REQUIRE(int32_type->equal(int32_type->from_string("2147483647"), int32_type->decompose((int32_t)2147483647))); BOOST_REQUIRE_EQUAL(int32_type->to_string(int32_type->decompose((int32_t)-2147483647)), "-2147483647"); - auto test_parsing_fails = [] (sstring text) { - try { - int32_type->from_string(text); - BOOST_FAIL(sprint("Parsing of '%s' should have failed", text)); - } catch (const marshal_exception& e) { - // expected - } - }; - - test_parsing_fails("asd"); - test_parsing_fails("-2147483649"); - test_parsing_fails("2147483648"); - test_parsing_fails("2147483648123"); + test_parsing_fails(int32_type, "asd"); + test_parsing_fails(int32_type, "-2147483649"); + test_parsing_fails(int32_type, "2147483648"); + test_parsing_fails(int32_type, "2147483648123"); BOOST_REQUIRE_EQUAL(int32_type->to_string(bytes()), ""); } +BOOST_AUTO_TEST_CASE(test_timeuuid_type_string_conversions) { + auto now = utils::UUID_gen::get_time_UUID(); + BOOST_REQUIRE(timeuuid_type->equal(timeuuid_type->from_string(now.to_sstring()), timeuuid_type->decompose(now))); + auto uuid = utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3")); + BOOST_REQUIRE(timeuuid_type->equal(timeuuid_type->from_string("D2177dD0-EAa2-11de-a572-001B779C76e3"), timeuuid_type->decompose(uuid))); + + test_parsing_fails(timeuuid_type, "something"); + test_parsing_fails(timeuuid_type, "D2177dD0-EAa2-11de-a572-001B779C76e3a"); + test_parsing_fails(timeuuid_type, "D2177dD0-EAa2-11de-a572001-B779C76e3"); + test_parsing_fails(timeuuid_type, "D2177dD0EAa211dea572001B779C76e3"); + test_parsing_fails(timeuuid_type, utils::make_random_uuid().to_sstring()); +} + +BOOST_AUTO_TEST_CASE(test_uuid_type_string_conversions) { + auto now = utils::UUID_gen::get_time_UUID(); + BOOST_REQUIRE(uuid_type->equal(uuid_type->from_string(now.to_sstring()), uuid_type->decompose(now))); + auto random = utils::make_random_uuid(); + BOOST_REQUIRE(uuid_type->equal(uuid_type->from_string(random.to_sstring()), uuid_type->decompose(random))); + auto uuid = utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3")); + BOOST_REQUIRE(uuid_type->equal(uuid_type->from_string("D2177dD0-EAa2-11de-a572-001B779C76e3"), uuid_type->decompose(uuid))); + + test_parsing_fails(uuid_type, "something"); + test_parsing_fails(uuid_type, "D2177dD0-EAa2-11de-a572-001B779C76e3a"); + test_parsing_fails(uuid_type, "D2177dD0-EAa2-11de-a572001-B779C76e3"); + test_parsing_fails(uuid_type, "D2177dD0EAa211dea572001B779C76e3"); +} + +BOOST_AUTO_TEST_CASE(test_inet_type_string_conversions) { + net::ipv4_address addr("127.0.0.1"); + BOOST_REQUIRE(inet_addr_type->equal(inet_addr_type->from_string("127.0.0.1"), inet_addr_type->decompose(addr))); + + test_parsing_fails(inet_addr_type, "something"); + test_parsing_fails(inet_addr_type, "300.127.127.127"); + test_parsing_fails(inet_addr_type, "127-127.127.127"); + test_parsing_fails(inet_addr_type, "127.127.127.127.127"); +} + +BOOST_AUTO_TEST_CASE(test_timestamp_type_string_conversions) { + timestamp_type->from_string("now"); + db_clock::time_point tp(db_clock::duration(1435881600000)); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("1435881600000"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03+0000"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03-00"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 00:00+0000"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 01:00:00+0000"), timestamp_type->decompose(tp + 1h))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 01:02:03.123+0000"), timestamp_type->decompose(tp + 123ms + 1h + 2min + 3s))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 12:30:00+1230"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 12:00:00+12"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03 12:30:00+12:30"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-02 23:00-0100"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03T00:00+0000"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03T01:00:00+0000"), timestamp_type->decompose(tp + 1h))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03T00:00:00.123+0000"), timestamp_type->decompose(tp + 123ms))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-03T12:30:00+1230"), timestamp_type->decompose(tp))); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string("2015-07-02T23:00-0100"), timestamp_type->decompose(tp))); + + auto now = time(nullptr); + auto local_now = *localtime(&now); + char buf[100]; + db_clock::time_point now_tp(db_clock::duration(now * 1000)); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S%z", &local_now); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string(buf), timestamp_type->decompose(now_tp))); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &local_now); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string(buf), timestamp_type->decompose(now_tp))); + + struct tm dst = { 0 }; + dst.tm_isdst = -1; + dst.tm_year = 2015 - 1900; + dst.tm_mon = 1 - 1; + dst.tm_mday = 2; + dst.tm_hour = 3; + dst.tm_min = 4; + dst.tm_sec = 5; + auto dst_jan = db_clock::from_time_t(mktime(&dst)); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &dst); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string(buf), timestamp_type->decompose(dst_jan))); + + dst.tm_isdst = -1; + dst.tm_mon = 6 - 1; + auto dst_jun = db_clock::from_time_t(mktime(&dst)); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &dst); + BOOST_REQUIRE(timestamp_type->equal(timestamp_type->from_string(buf), timestamp_type->decompose(dst_jun))); + + test_parsing_fails(timestamp_type, "something"); + test_parsing_fails(timestamp_type, "2001-99-01"); + test_parsing_fails(timestamp_type, "2001-01-01 12:00:00.0a"); + test_parsing_fails(timestamp_type, "2001-01-01 12:00p0000"); + test_parsing_fails(timestamp_type, "2001-01-01 12:00+1200a"); +} + BOOST_AUTO_TEST_CASE(test_boolean_type_string_conversions) { BOOST_REQUIRE(boolean_type->equal(boolean_type->from_string(""), boolean_type->decompose(false))); BOOST_REQUIRE(boolean_type->equal(boolean_type->from_string("false"), boolean_type->decompose(false))); diff --git a/transport/server.cc b/transport/server.cc index 01f9fcc6ee..dbe613b962 100644 --- a/transport/server.cc +++ b/transport/server.cc @@ -358,6 +358,8 @@ future<> cql_server::connection::process_request() { }).then_wrapped([stream = f.stream, this] (future<> f) { try { f.get(); + } catch (const exceptions::cassandra_exception& ex) { + write_error(stream, ex.code(), ex.what()); } catch (std::exception& ex) { write_error(stream, exceptions::exception_code::SERVER_ERROR, ex.what()); } catch (...) { diff --git a/types.cc b/types.cc index 366355b7f6..42f2775ac1 100644 --- a/types.cc +++ b/types.cc @@ -13,9 +13,12 @@ #include "combine.hh" #include #include +#include #include #include #include +#include +#include template struct simple_type_traits { @@ -337,10 +340,25 @@ struct timeuuid_type_impl : public abstract_type { return std::hash()(v); } virtual bytes from_string(sstring_view s) const override { - throw std::runtime_error("not implemented"); + if (s.empty()) { + return bytes(); + } + static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"); + if (!std::regex_match(s.data(), re)) { + throw marshal_exception(); + } + utils::UUID v(s); + if (v.version() != 1) { + throw marshal_exception(); + } + return v.to_bytes(); } virtual sstring to_string(const bytes& b) const override { - throw std::runtime_error("not implemented"); + auto v = deserialize(b); + if (v.empty()) { + return ""; + } + return boost::any_cast(v).to_sstring(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::timeuuid; @@ -381,8 +399,90 @@ struct timestamp_type_impl : simple_type_impl { return boost::any(db_clock::time_point(db_clock::duration(v))); } // FIXME: isCompatibleWith(timestampuuid) + boost::posix_time::ptime get_time(const std::string& s) const { + // Apparently, the code below doesn't leak the input facet. + // std::locale::facet has some internal, custom reference counting + // and deletes the object when it's no longer used. + auto tif = new boost::posix_time::time_input_facet("%Y-%m-%d %H:%M:%S%F"); + std::istringstream ss(s); + ss.imbue(std::locale(ss.getloc(), tif)); + boost::posix_time::ptime t; + ss >> t; + if (ss.fail() || ss.peek() != std::istringstream::traits_type::eof()) { + throw marshal_exception(); + } + return t; + } + boost::posix_time::time_duration get_utc_offset(const std::string& s) const { + static constexpr const char* formats[] = { + "%H:%M", + "%H%M", + }; + for (auto&& f : formats) { + auto tif = new boost::posix_time::time_input_facet(f); + std::istringstream ss(s); + ss.imbue(std::locale(ss.getloc(), tif)); + auto sign = ss.get(); + boost::posix_time::ptime p; + ss >> p; + if (ss.good() && ss.peek() == std::istringstream::traits_type::eof()) { + return p.time_of_day() * (sign == '-' ? -1 : 1); + } + } + throw marshal_exception(); + } + int64_t timestamp_from_string(sstring_view s) const { + std::string str; + str.resize(s.size()); + std::transform(s.begin(), s.end(), str.begin(), ::tolower); + if (str == "now") { + return db_clock::now().time_since_epoch().count(); + } + + char* end; + auto v = std::strtoll(s.begin(), &end, 10); + if (end == s.begin() + s.size()) { + return v; + } + + std::regex date_re("^\\d{4}-\\d{2}-\\d{2}([ t]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?)?"); + std::smatch dsm; + if (!std::regex_search(str, dsm, date_re)) { + throw marshal_exception(); + } + auto t = get_time(dsm.str()); + + auto tz = dsm.suffix().str(); + std::regex tz_re("([\\+-]\\d{2}:?(\\d{2})?)"); + std::smatch tsm; + if (std::regex_match(tz, tsm, tz_re)) { + t -= get_utc_offset(tsm.str()); + } else if (tz.empty()) { + typedef boost::date_time::c_local_adjustor local_tz; + // local_tz::local_to_utc(), where are you? + auto t1 = local_tz::utc_to_local(t); + auto tz_offset = t1 - t; + auto t2 = local_tz::utc_to_local(t - tz_offset); + auto dst_offset = t2 - t; + t -= tz_offset + dst_offset; + } else { + throw marshal_exception(); + } + return (t - boost::posix_time::from_time_t(0)).total_milliseconds(); + } virtual bytes from_string(sstring_view s) const override { - throw std::runtime_error("not implemented"); + if (s.empty()) { + return bytes(); + } + int64_t ts; + try { + ts = timestamp_from_string(s); + } catch (...) { + throw marshal_exception(sprint("unable to parse date '%s'", s)); + } + bytes b(bytes::initialized_later(), sizeof(int64_t)); + *unaligned_cast(b.begin()) = net::hton(ts); + return b; } virtual sstring to_string(const bytes& b) const override { throw std::runtime_error("not implemented"); @@ -447,10 +547,22 @@ struct uuid_type_impl : abstract_type { return std::hash()(v); } virtual bytes from_string(sstring_view s) const override { - throw std::runtime_error("not implemented"); + if (s.empty()) { + return bytes(); + } + static const std::regex re("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"); + if (!std::regex_match(s.data(), re)) { + throw marshal_exception(); + } + utils::UUID v(s); + return v.to_bytes(); } virtual sstring to_string(const bytes& b) const override { - throw std::runtime_error("not implemented"); + auto v = deserialize(b); + if (v.empty()) { + return ""; + } + return boost::any_cast(v).to_sstring(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::uuid; @@ -497,10 +609,28 @@ struct inet_addr_type_impl : abstract_type { return std::hash()(v); } virtual bytes from_string(sstring_view s) const override { - throw std::runtime_error("not implemented"); + // FIXME: support host names + if (s.empty()) { + return bytes(); + } + net::ipv4_address ipv4; + try { + ipv4 = net::ipv4_address(s.data()); + } catch (...) { + throw marshal_exception(); + } + bytes b(bytes::initialized_later(), sizeof(uint32_t)); + auto out = b.begin(); + serialize(boost::any(ipv4), out); + return b; } virtual sstring to_string(const bytes& b) const override { - throw std::runtime_error("not implemented"); + auto v = deserialize(b); + if (v.empty()) { + return ""; + } + boost::asio::ip::address_v4 ipv4(boost::any_cast(v).ip); + return ipv4.to_string(); } virtual ::shared_ptr as_cql3_type() const override { return cql3::cql3_type::inet; @@ -573,10 +703,26 @@ struct floating_type_impl : public simple_type_impl { return boost::any(x.d); } virtual bytes from_string(sstring_view s) const override { - throw std::runtime_error("not implemented"); + if (s.empty()) { + return bytes(); + } + try { + auto d = boost::lexical_cast(s.begin(), s.size()); + bytes b(bytes::initialized_later(), sizeof(T)); + auto out = b.begin(); + serialize(boost::any(d), out); + return b; + } + catch(const boost::bad_lexical_cast& e) { + throw marshal_exception(sprint("Invalid number format '%s'", s)); + } } virtual sstring to_string(const bytes& b) const override { - throw std::runtime_error("not implemented"); + auto v = deserialize(b); + if (v.empty()) { + return ""; + } + return to_sstring(boost::any_cast(v)); } }; diff --git a/utils/UUID.hh b/utils/UUID.hh index c82719da60..b7ef9d85e8 100644 --- a/utils/UUID.hh +++ b/utils/UUID.hh @@ -27,7 +27,8 @@ public: UUID() : most_sig_bits(0), least_sig_bits(0) {} UUID(int64_t most_sig_bits, int64_t least_sig_bits) : most_sig_bits(most_sig_bits), least_sig_bits(least_sig_bits) {} - explicit UUID(const sstring& uuid_string); + explicit UUID(const sstring& uuid_string) : UUID(sstring_view(uuid_string)) { } + explicit UUID(sstring_view uuid_string); int64_t get_most_significant_bits() const { return most_sig_bits; diff --git a/utils/uuid.cc b/utils/uuid.cc index 5f69bb039a..9bb145fd11 100644 --- a/utils/uuid.cc +++ b/utils/uuid.cc @@ -40,11 +40,13 @@ std::ostream& operator<<(std::ostream& out, const UUID& uuid) { return out << uuid.to_sstring(); } -UUID::UUID(const sstring& uuid) { - auto uuid_string = uuid; +UUID::UUID(sstring_view uuid) { + sstring uuid_string(uuid.begin(), uuid.end()); boost::erase_all(uuid_string, "-"); auto size = uuid_string.size() / 2; - assert(size == 16); + if (size != 16) { + throw marshal_exception(); + } sstring most = sstring(uuid_string.begin(), uuid_string.begin() + size); sstring least = sstring(uuid_string.begin() + size, uuid_string.end()); int base = 16;