Skip to content

Commit 50d4d5e

Browse files
authored
feat(spanner): enable UUID data type (#15867)
1 parent 28fc4b9 commit 50d4d5e

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

google/cloud/spanner/integration_tests/data_types_integration_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,24 @@ TEST_F(PgDataTypeIntegrationTest, WriteReadNumeric) {
388388
EXPECT_THAT(result, IsOkAndHolds(UnorderedElementsAreArray(data)));
389389
}
390390

391+
TEST_F(DataTypeIntegrationTest, WriteReadUuid) {
392+
auto uuid1 = MakeUuid("{DECAFBAD-DEAD-FADE-CAFE-FEEDFACEBEEF}");
393+
ASSERT_STATUS_OK(uuid1);
394+
auto uuid2 = MakeUuid("0b6ed04ca16dfc4652817f9978c13738");
395+
ASSERT_STATUS_OK(uuid2);
396+
397+
std::vector<Uuid> const data = {
398+
Uuid(0), Uuid(1), *uuid1, *uuid2, Uuid(37, 42),
399+
};
400+
auto result = WriteReadData(*client_, data, "UuidValue");
401+
402+
if (UsingEmulator()) {
403+
EXPECT_THAT(result, StatusIs(StatusCode::kNotFound));
404+
} else {
405+
EXPECT_THAT(result, IsOkAndHolds(UnorderedElementsAreArray(data)));
406+
}
407+
}
408+
391409
TEST_F(DataTypeIntegrationTest, WriteReadProtoEnum) {
392410
std::vector<ProtoEnum<testing::Genre>> const data = {
393411
testing::Genre::POP,

google/cloud/spanner/testing/database_integration_test.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ void DatabaseIntegrationTest::SetUpTestSuite() {
9494
DateValue DATE,
9595
JsonValue JSON,
9696
NumericValue NUMERIC,
97+
)sql",
98+
(emulator_ ? "" : R"sql(
99+
UuidValue UUID,
100+
)sql"),
101+
R"sql(
97102
ArrayBoolValue ARRAY<BOOL>,
98103
ArrayInt64Value ARRAY<INT64>,
99104
ArrayFloat64Value ARRAY<FLOAT64>,
@@ -107,7 +112,12 @@ void DatabaseIntegrationTest::SetUpTestSuite() {
107112
ArrayTimestampValue ARRAY<TIMESTAMP>,
108113
ArrayDateValue ARRAY<DATE>,
109114
ArrayJsonValue ARRAY<JSON>,
110-
ArrayNumericValue ARRAY<NUMERIC>
115+
ArrayNumericValue ARRAY<NUMERIC>,
116+
)sql",
117+
(emulator_ ? "" : R"sql(
118+
ArrayUuidValue ARRAY<UUID>
119+
)sql"),
120+
R"sql(
111121
) PRIMARY KEY (Id)
112122
)sql"));
113123
if (!emulator_) { // proto columns

google/cloud/spanner/value.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
159159
case google::spanner::v1::TypeCode::TIMESTAMP:
160160
case google::spanner::v1::TypeCode::NUMERIC:
161161
case google::spanner::v1::TypeCode::INTERVAL:
162+
case google::spanner::v1::TypeCode::UUID:
162163
return os << v.string_value();
163164

164165
case google::spanner::v1::TypeCode::DATE:
@@ -274,6 +275,10 @@ bool Value::TypeProtoIs(Interval, google::spanner::v1::Type const& type) {
274275
return type.code() == google::spanner::v1::TypeCode::INTERVAL;
275276
}
276277

278+
bool Value::TypeProtoIs(Uuid, google::spanner::v1::Type const& type) {
279+
return type.code() == google::spanner::v1::TypeCode::UUID;
280+
}
281+
277282
bool Value::TypeProtoIs(std::string const&,
278283
google::spanner::v1::Type const& type) {
279284
return type.code() == google::spanner::v1::TypeCode::STRING;
@@ -415,6 +420,12 @@ google::spanner::v1::Type Value::MakeTypeProto(Interval) {
415420
return t;
416421
}
417422

423+
google::spanner::v1::Type Value::MakeTypeProto(Uuid) {
424+
google::spanner::v1::Type t;
425+
t.set_code(google::spanner::v1::TypeCode::UUID);
426+
return t;
427+
}
428+
418429
google::spanner::v1::Type Value::MakeTypeProto(int) {
419430
return MakeTypeProto(std::int64_t{});
420431
}
@@ -537,6 +548,12 @@ google::protobuf::Value Value::MakeValueProto(Interval intvl) {
537548
return v;
538549
}
539550

551+
google::protobuf::Value Value::MakeValueProto(Uuid u) {
552+
google::protobuf::Value v;
553+
v.set_string_value(std::string(u));
554+
return v;
555+
}
556+
540557
google::protobuf::Value Value::MakeValueProto(int i) {
541558
return MakeValueProto(std::int64_t{i});
542559
}
@@ -731,11 +748,19 @@ StatusOr<absl::CivilDay> Value::GetValue(absl::CivilDay,
731748
StatusOr<Interval> Value::GetValue(Interval, google::protobuf::Value const& pv,
732749
google::spanner::v1::Type const&) {
733750
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
734-
return Status(StatusCode::kUnknown, "missing Interval");
751+
return internal::UnknownError("missing Interval", GCP_ERROR_INFO());
735752
}
736753
return MakeInterval(pv.string_value());
737754
}
738755

756+
StatusOr<Uuid> Value::GetValue(Uuid, google::protobuf::Value const& pv,
757+
google::spanner::v1::Type const&) {
758+
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
759+
return internal::UnknownError("missing UUID", GCP_ERROR_INFO());
760+
}
761+
return MakeUuid(pv.string_value());
762+
}
763+
739764
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
740765
} // namespace spanner
741766
} // namespace cloud

google/cloud/spanner/value.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "google/cloud/spanner/proto_enum.h"
2626
#include "google/cloud/spanner/proto_message.h"
2727
#include "google/cloud/spanner/timestamp.h"
28+
#include "google/cloud/spanner/uuid.h"
2829
#include "google/cloud/spanner/version.h"
2930
#include "google/cloud/internal/base64_transforms.h"
3031
#include "google/cloud/internal/make_status.h"
@@ -79,6 +80,7 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
7980
* TIMESTAMP | `google::cloud::spanner::Timestamp`
8081
* DATE | `absl::CivilDay`
8182
* INTERVAL | `google::cloud::spanner::Interval`
83+
* UUID | `google::cloud::spanner::Uuid`
8284
* ENUM | `google::cloud::spanner::ProtoEnum<E>`
8385
* PROTO | `google::cloud::spanner::ProtoMessage<M>`
8486
* ARRAY | `std::vector<T>` // [1]
@@ -226,6 +228,8 @@ class Value {
226228
/// @copydoc Value(bool)
227229
explicit Value(Interval v) : Value(PrivateConstructor{}, std::move(v)) {}
228230
/// @copydoc Value(bool)
231+
explicit Value(Uuid v) : Value(PrivateConstructor{}, std::move(v)) {}
232+
/// @copydoc Value(bool)
229233
template <typename E>
230234
explicit Value(ProtoEnum<E> v) : Value(PrivateConstructor{}, std::move(v)) {}
231235
/// @copydoc Value(bool)
@@ -392,6 +396,7 @@ class Value {
392396
static bool TypeProtoIs(CommitTimestamp, google::spanner::v1::Type const&);
393397
static bool TypeProtoIs(absl::CivilDay, google::spanner::v1::Type const&);
394398
static bool TypeProtoIs(Interval, google::spanner::v1::Type const&);
399+
static bool TypeProtoIs(Uuid, google::spanner::v1::Type const&);
395400
static bool TypeProtoIs(std::string const&, google::spanner::v1::Type const&);
396401
static bool TypeProtoIs(Bytes const&, google::spanner::v1::Type const&);
397402
static bool TypeProtoIs(Json const&, google::spanner::v1::Type const&);
@@ -466,6 +471,8 @@ class Value {
466471
static google::spanner::v1::Type MakeTypeProto(CommitTimestamp);
467472
static google::spanner::v1::Type MakeTypeProto(absl::CivilDay);
468473
static google::spanner::v1::Type MakeTypeProto(Interval);
474+
static google::spanner::v1::Type MakeTypeProto(Uuid);
475+
469476
template <typename E>
470477
static google::spanner::v1::Type MakeTypeProto(ProtoEnum<E>) {
471478
google::spanner::v1::Type t;
@@ -546,6 +553,8 @@ class Value {
546553
static google::protobuf::Value MakeValueProto(CommitTimestamp ts);
547554
static google::protobuf::Value MakeValueProto(absl::CivilDay d);
548555
static google::protobuf::Value MakeValueProto(Interval intvl);
556+
static google::protobuf::Value MakeValueProto(Uuid u);
557+
549558
template <typename E>
550559
static google::protobuf::Value MakeValueProto(ProtoEnum<E> e) {
551560
return MakeValueProto(std::int64_t{E{e}});
@@ -638,6 +647,8 @@ class Value {
638647
google::spanner::v1::Type const&);
639648
static StatusOr<Interval> GetValue(Interval, google::protobuf::Value const&,
640649
google::spanner::v1::Type const&);
650+
static StatusOr<Uuid> GetValue(Uuid, google::protobuf::Value const&,
651+
google::spanner::v1::Type const&);
641652
template <typename E>
642653
static StatusOr<ProtoEnum<E>> GetValue(ProtoEnum<E>,
643654
google::protobuf::Value const& pv,

google/cloud/spanner/value_test.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ TEST(Value, BasicSemantics) {
273273
TestBasicSemantics(v);
274274
}
275275

276+
for (auto x : {Uuid()}) {
277+
SCOPED_TRACE("Testing: google::cloud::spanner::Uuid " + std::string{x});
278+
TestBasicSemantics(x);
279+
TestBasicSemantics(std::vector<Uuid>(5, x));
280+
std::vector<absl::optional<Uuid>> v(5, x);
281+
v.resize(10);
282+
TestBasicSemantics(v);
283+
}
284+
276285
for (auto x : {testing::Genre::POP, testing::Genre::JAZZ,
277286
testing::Genre::FOLK, testing::Genre::ROCK}) {
278287
SCOPED_TRACE("Testing: ProtoEnum<testing::Genre> " +
@@ -949,6 +958,20 @@ TEST(Value, ProtoConversionInterval) {
949958
}
950959
}
951960

961+
TEST(Value, ProtoConversionUuid) {
962+
for (auto const& x : std::vector<Uuid>{
963+
Uuid(),
964+
Uuid(0x7bf8a7b819171919, 0x2625f208c5824254),
965+
MakeUuid("{0b6ed04ca16dfc4652817f9978c13738}").value(),
966+
}) {
967+
Value const v(x);
968+
auto const p = spanner_internal::ToProto(v);
969+
EXPECT_EQ(v, spanner_internal::FromProto(p.first, p.second));
970+
EXPECT_EQ(google::spanner::v1::TypeCode::UUID, p.first.code());
971+
EXPECT_EQ(std::string{x}, p.second.string_value());
972+
}
973+
}
974+
952975
TEST(Value, ProtoConversionProtoEnum) {
953976
for (auto e : {testing::Genre::POP, testing::Genre::JAZZ,
954977
testing::Genre::FOLK, testing::Genre::ROCK}) {
@@ -1296,6 +1319,24 @@ TEST(Value, GetBadInterval) {
12961319
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
12971320
}
12981321

1322+
TEST(Value, GetBadUuid) {
1323+
Value v(Uuid{});
1324+
ClearProtoKind(v);
1325+
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
1326+
1327+
SetProtoKind(v, google::protobuf::NULL_VALUE);
1328+
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
1329+
1330+
SetProtoKind(v, true);
1331+
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
1332+
1333+
SetProtoKind(v, 0.0);
1334+
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
1335+
1336+
SetProtoKind(v, "blah");
1337+
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
1338+
}
1339+
12991340
TEST(Value, GetBadProtoEnum) {
13001341
Value v(ProtoEnum<testing::Genre>{});
13011342
ClearProtoKind(v);
@@ -1475,6 +1516,7 @@ TEST(Value, OutputStream) {
14751516
{Value(absl::CivilDay()), "1970-01-01", normal},
14761517
{Value(Timestamp()), "1970-01-01T00:00:00Z", normal},
14771518
{Value(Interval()), "P0D", normal},
1519+
{Value(Uuid()), "00000000-0000-0000-0000-000000000000", normal},
14781520
{Value(ProtoEnum<testing::Genre>(testing::Genre::POP)),
14791521
"google.cloud.spanner.testing.POP", normal},
14801522
{Value(ProtoMessage<testing::SingerInfo>(singer)),
@@ -1509,6 +1551,7 @@ TEST(Value, OutputStream) {
15091551
{MakeNullValue<absl::CivilDay>(), "NULL", normal},
15101552
{MakeNullValue<Timestamp>(), "NULL", normal},
15111553
{MakeNullValue<Interval>(), "NULL", normal},
1554+
{MakeNullValue<Uuid>(), "NULL", normal},
15121555
{MakeNullValue<ProtoEnum<testing::Genre>>(), "NULL", normal},
15131556
{MakeNullValue<ProtoMessage<testing::SingerInfo>>(), "NULL", normal},
15141557

@@ -1530,6 +1573,8 @@ TEST(Value, OutputStream) {
15301573
normal},
15311574
{Value(std::vector<Timestamp>{1}), "[1970-01-01T00:00:00Z]", normal},
15321575
{Value(std::vector<Interval>{1}), "[P0D]", normal},
1576+
{Value(std::vector<Uuid>{1}), "[00000000-0000-0000-0000-000000000000]",
1577+
normal},
15331578
{Value(std::vector<ProtoEnum<testing::Genre>>{testing::JAZZ,
15341579
testing::FOLK}),
15351580
"[google.cloud.spanner.testing.JAZZ, google.cloud.spanner.testing.FOLK]",
@@ -1556,6 +1601,7 @@ TEST(Value, OutputStream) {
15561601
{MakeNullValue<std::vector<absl::CivilDay>>(), "NULL", normal},
15571602
{MakeNullValue<std::vector<Timestamp>>(), "NULL", normal},
15581603
{MakeNullValue<std::vector<Interval>>(), "NULL", normal},
1604+
{MakeNullValue<std::vector<Uuid>>(), "NULL", normal},
15591605
{MakeNullValue<std::vector<ProtoEnum<testing::Genre>>>(), "NULL", normal},
15601606
{MakeNullValue<std::vector<ProtoMessage<testing::SingerInfo>>>(), "NULL",
15611607
normal},
@@ -1710,6 +1756,11 @@ TEST(Value, OutputStreamMatchesT) {
17101756
StreamMatchesValueStream(Interval());
17111757
StreamMatchesValueStream(MakeInterval("P1Y2M3DT4H5M6.789S").value());
17121758

1759+
// Uuid
1760+
StreamMatchesValueStream(Uuid());
1761+
StreamMatchesValueStream(
1762+
MakeUuid("{0b6ed04ca16dfc4652817f9978c13738}").value());
1763+
17131764
// ProtoEnum
17141765
StreamMatchesValueStream(ProtoEnum<testing::Genre>());
17151766
StreamMatchesValueStream(ProtoEnum<testing::Genre>(testing::ROCK));

0 commit comments

Comments
 (0)