diff --git a/google/cloud/spanner/value.cc b/google/cloud/spanner/value.cc index d7b5ec515d438..b0e18fc755084 100644 --- a/google/cloud/spanner/value.cc +++ b/google/cloud/spanner/value.cc @@ -159,6 +159,7 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion) case google::spanner::v1::TypeCode::TIMESTAMP: case google::spanner::v1::TypeCode::NUMERIC: case google::spanner::v1::TypeCode::INTERVAL: + case google::spanner::v1::TypeCode::UUID: return os << v.string_value(); case google::spanner::v1::TypeCode::DATE: @@ -274,6 +275,10 @@ bool Value::TypeProtoIs(Interval, google::spanner::v1::Type const& type) { return type.code() == google::spanner::v1::TypeCode::INTERVAL; } +bool Value::TypeProtoIs(Uuid, google::spanner::v1::Type const& type) { + return type.code() == google::spanner::v1::TypeCode::UUID; +} + bool Value::TypeProtoIs(std::string const&, google::spanner::v1::Type const& type) { return type.code() == google::spanner::v1::TypeCode::STRING; @@ -415,6 +420,12 @@ google::spanner::v1::Type Value::MakeTypeProto(Interval) { return t; } +google::spanner::v1::Type Value::MakeTypeProto(Uuid) { + google::spanner::v1::Type t; + t.set_code(google::spanner::v1::TypeCode::UUID); + return t; +} + google::spanner::v1::Type Value::MakeTypeProto(int) { return MakeTypeProto(std::int64_t{}); } @@ -537,6 +548,12 @@ google::protobuf::Value Value::MakeValueProto(Interval intvl) { return v; } +google::protobuf::Value Value::MakeValueProto(Uuid u) { + google::protobuf::Value v; + v.set_string_value(std::string(u)); + return v; +} + google::protobuf::Value Value::MakeValueProto(int i) { return MakeValueProto(std::int64_t{i}); } @@ -736,6 +753,14 @@ StatusOr Value::GetValue(Interval, google::protobuf::Value const& pv, return MakeInterval(pv.string_value()); } +StatusOr Value::GetValue(Uuid, google::protobuf::Value const& pv, + google::spanner::v1::Type const&) { + if (pv.kind_case() != google::protobuf::Value::kStringValue) { + return Status(StatusCode::kUnknown, "missing UUID"); + } + return MakeUuid(pv.string_value()); +} + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace spanner } // namespace cloud diff --git a/google/cloud/spanner/value.h b/google/cloud/spanner/value.h index d888c4876a547..cade7ba9d58d6 100644 --- a/google/cloud/spanner/value.h +++ b/google/cloud/spanner/value.h @@ -25,6 +25,7 @@ #include "google/cloud/spanner/proto_enum.h" #include "google/cloud/spanner/proto_message.h" #include "google/cloud/spanner/timestamp.h" +#include "google/cloud/spanner/uuid.h" #include "google/cloud/spanner/version.h" #include "google/cloud/internal/base64_transforms.h" #include "google/cloud/internal/make_status.h" @@ -79,6 +80,7 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN * TIMESTAMP | `google::cloud::spanner::Timestamp` * DATE | `absl::CivilDay` * INTERVAL | `google::cloud::spanner::Interval` + * UUID | `google::cloud::spanner::Uuid` * ENUM | `google::cloud::spanner::ProtoEnum` * PROTO | `google::cloud::spanner::ProtoMessage` * ARRAY | `std::vector` // [1] @@ -225,6 +227,7 @@ class Value { : Value(PrivateConstructor{}, std::move(v)) {} /// @copydoc Value(bool) explicit Value(Interval v) : Value(PrivateConstructor{}, std::move(v)) {} + explicit Value(Uuid v) : Value(PrivateConstructor{}, std::move(v)) {} /// @copydoc Value(bool) template explicit Value(ProtoEnum v) : Value(PrivateConstructor{}, std::move(v)) {} @@ -392,6 +395,7 @@ class Value { static bool TypeProtoIs(CommitTimestamp, google::spanner::v1::Type const&); static bool TypeProtoIs(absl::CivilDay, google::spanner::v1::Type const&); static bool TypeProtoIs(Interval, google::spanner::v1::Type const&); + static bool TypeProtoIs(Uuid, google::spanner::v1::Type const&); static bool TypeProtoIs(std::string const&, google::spanner::v1::Type const&); static bool TypeProtoIs(Bytes const&, google::spanner::v1::Type const&); static bool TypeProtoIs(Json const&, google::spanner::v1::Type const&); @@ -466,6 +470,7 @@ class Value { static google::spanner::v1::Type MakeTypeProto(CommitTimestamp); static google::spanner::v1::Type MakeTypeProto(absl::CivilDay); static google::spanner::v1::Type MakeTypeProto(Interval); + static google::spanner::v1::Type MakeTypeProto(Uuid); template static google::spanner::v1::Type MakeTypeProto(ProtoEnum) { google::spanner::v1::Type t; @@ -546,6 +551,7 @@ class Value { static google::protobuf::Value MakeValueProto(CommitTimestamp ts); static google::protobuf::Value MakeValueProto(absl::CivilDay d); static google::protobuf::Value MakeValueProto(Interval intvl); + static google::protobuf::Value MakeValueProto(Uuid u); template static google::protobuf::Value MakeValueProto(ProtoEnum e) { return MakeValueProto(std::int64_t{E{e}}); @@ -638,6 +644,8 @@ class Value { google::spanner::v1::Type const&); static StatusOr GetValue(Interval, google::protobuf::Value const&, google::spanner::v1::Type const&); + static StatusOr GetValue(Uuid, google::protobuf::Value const&, + google::spanner::v1::Type const&); template static StatusOr> GetValue(ProtoEnum, google::protobuf::Value const& pv, diff --git a/google/cloud/spanner/value_test.cc b/google/cloud/spanner/value_test.cc index c604e0778c870..7d68ff7a46923 100644 --- a/google/cloud/spanner/value_test.cc +++ b/google/cloud/spanner/value_test.cc @@ -273,6 +273,15 @@ TEST(Value, BasicSemantics) { TestBasicSemantics(v); } + for (auto x : {Uuid()}) { + SCOPED_TRACE("Testing: google::cloud::spanner::Uuid " + std::string{x}); + TestBasicSemantics(x); + TestBasicSemantics(std::vector(5, x)); + std::vector> v(5, x); + v.resize(10); + TestBasicSemantics(v); + } + for (auto x : {testing::Genre::POP, testing::Genre::JAZZ, testing::Genre::FOLK, testing::Genre::ROCK}) { SCOPED_TRACE("Testing: ProtoEnum " + @@ -949,6 +958,20 @@ TEST(Value, ProtoConversionInterval) { } } +TEST(Value, ProtoConversionUuid) { + for (auto const& x : std::vector{ + Uuid(), + Uuid(0x7bf8a7b819171919, 0x2625f208c5824254), + MakeUuid("{0b6ed04ca16dfc4652817f9978c13738}").value(), + }) { + Value const v(x); + auto const p = spanner_internal::ToProto(v); + EXPECT_EQ(v, spanner_internal::FromProto(p.first, p.second)); + EXPECT_EQ(google::spanner::v1::TypeCode::UUID, p.first.code()); + EXPECT_EQ(std::string{x}, p.second.string_value()); + } +} + TEST(Value, ProtoConversionProtoEnum) { for (auto e : {testing::Genre::POP, testing::Genre::JAZZ, testing::Genre::FOLK, testing::Genre::ROCK}) { @@ -1296,6 +1319,24 @@ TEST(Value, GetBadInterval) { EXPECT_THAT(v.get(), Not(IsOk())); } +TEST(Value, GetBadUuid) { + Value v(Uuid{}); + ClearProtoKind(v); + EXPECT_THAT(v.get(), Not(IsOk())); + + SetProtoKind(v, google::protobuf::NULL_VALUE); + EXPECT_THAT(v.get(), Not(IsOk())); + + SetProtoKind(v, true); + EXPECT_THAT(v.get(), Not(IsOk())); + + SetProtoKind(v, 0.0); + EXPECT_THAT(v.get(), Not(IsOk())); + + SetProtoKind(v, "blah"); + EXPECT_THAT(v.get(), Not(IsOk())); +} + TEST(Value, GetBadProtoEnum) { Value v(ProtoEnum{}); ClearProtoKind(v); @@ -1475,6 +1516,7 @@ TEST(Value, OutputStream) { {Value(absl::CivilDay()), "1970-01-01", normal}, {Value(Timestamp()), "1970-01-01T00:00:00Z", normal}, {Value(Interval()), "P0D", normal}, + {Value(Uuid()), "00000000-0000-0000-0000-000000000000", normal}, {Value(ProtoEnum(testing::Genre::POP)), "google.cloud.spanner.testing.POP", normal}, {Value(ProtoMessage(singer)), @@ -1509,6 +1551,7 @@ TEST(Value, OutputStream) { {MakeNullValue(), "NULL", normal}, {MakeNullValue(), "NULL", normal}, {MakeNullValue(), "NULL", normal}, + {MakeNullValue(), "NULL", normal}, {MakeNullValue>(), "NULL", normal}, {MakeNullValue>(), "NULL", normal}, @@ -1530,6 +1573,8 @@ TEST(Value, OutputStream) { normal}, {Value(std::vector{1}), "[1970-01-01T00:00:00Z]", normal}, {Value(std::vector{1}), "[P0D]", normal}, + {Value(std::vector{1}), "[00000000-0000-0000-0000-000000000000]", + normal}, {Value(std::vector>{testing::JAZZ, testing::FOLK}), "[google.cloud.spanner.testing.JAZZ, google.cloud.spanner.testing.FOLK]", @@ -1556,6 +1601,7 @@ TEST(Value, OutputStream) { {MakeNullValue>(), "NULL", normal}, {MakeNullValue>(), "NULL", normal}, {MakeNullValue>(), "NULL", normal}, + {MakeNullValue>(), "NULL", normal}, {MakeNullValue>>(), "NULL", normal}, {MakeNullValue>>(), "NULL", normal}, @@ -1710,6 +1756,11 @@ TEST(Value, OutputStreamMatchesT) { StreamMatchesValueStream(Interval()); StreamMatchesValueStream(MakeInterval("P1Y2M3DT4H5M6.789S").value()); + // Uuid + StreamMatchesValueStream(Uuid()); + StreamMatchesValueStream( + MakeUuid("{0b6ed04ca16dfc4652817f9978c13738}").value()); + // ProtoEnum StreamMatchesValueStream(ProtoEnum()); StreamMatchesValueStream(ProtoEnum(testing::ROCK));