Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,24 @@ TEST_F(PgDataTypeIntegrationTest, WriteReadNumeric) {
EXPECT_THAT(result, IsOkAndHolds(UnorderedElementsAreArray(data)));
}

TEST_F(DataTypeIntegrationTest, WriteReadUuid) {
auto uuid1 = MakeUuid("{DECAFBAD-DEAD-FADE-CAFE-FEEDFACEBEEF}");
ASSERT_STATUS_OK(uuid1);
auto uuid2 = MakeUuid("0b6ed04ca16dfc4652817f9978c13738");
ASSERT_STATUS_OK(uuid2);

std::vector<Uuid> const data = {
Uuid(0), Uuid(1), *uuid1, *uuid2, Uuid(37, 42),
};
auto result = WriteReadData(*client_, data, "UuidValue");

if (UsingEmulator()) {
EXPECT_THAT(result, StatusIs(StatusCode::kNotFound));
} else {
EXPECT_THAT(result, IsOkAndHolds(UnorderedElementsAreArray(data)));
}
}

TEST_F(DataTypeIntegrationTest, WriteReadProtoEnum) {
std::vector<ProtoEnum<testing::Genre>> const data = {
testing::Genre::POP,
Expand Down
12 changes: 11 additions & 1 deletion google/cloud/spanner/testing/database_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ void DatabaseIntegrationTest::SetUpTestSuite() {
DateValue DATE,
JsonValue JSON,
NumericValue NUMERIC,
)sql",
(emulator_ ? "" : R"sql(
UuidValue UUID,
)sql"),
R"sql(
ArrayBoolValue ARRAY<BOOL>,
ArrayInt64Value ARRAY<INT64>,
ArrayFloat64Value ARRAY<FLOAT64>,
Expand All @@ -107,7 +112,12 @@ void DatabaseIntegrationTest::SetUpTestSuite() {
ArrayTimestampValue ARRAY<TIMESTAMP>,
ArrayDateValue ARRAY<DATE>,
ArrayJsonValue ARRAY<JSON>,
ArrayNumericValue ARRAY<NUMERIC>
ArrayNumericValue ARRAY<NUMERIC>,
)sql",
(emulator_ ? "" : R"sql(
ArrayUuidValue ARRAY<UUID>
)sql"),
R"sql(
) PRIMARY KEY (Id)
)sql"));
if (!emulator_) { // proto columns
Expand Down
27 changes: 26 additions & 1 deletion google/cloud/spanner/value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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{});
}
Expand Down Expand Up @@ -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});
}
Expand Down Expand Up @@ -731,11 +748,19 @@ StatusOr<absl::CivilDay> Value::GetValue(absl::CivilDay,
StatusOr<Interval> Value::GetValue(Interval, google::protobuf::Value const& pv,
google::spanner::v1::Type const&) {
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
return Status(StatusCode::kUnknown, "missing Interval");
return internal::UnknownError("missing Interval", GCP_ERROR_INFO());
}
return MakeInterval(pv.string_value());
}

StatusOr<Uuid> Value::GetValue(Uuid, google::protobuf::Value const& pv,
google::spanner::v1::Type const&) {
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
return internal::UnknownError("missing UUID", GCP_ERROR_INFO());
}
return MakeUuid(pv.string_value());
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace spanner
} // namespace cloud
Expand Down
11 changes: 11 additions & 0 deletions google/cloud/spanner/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<E>`
* PROTO | `google::cloud::spanner::ProtoMessage<M>`
* ARRAY | `std::vector<T>` // [1]
Expand Down Expand Up @@ -226,6 +228,8 @@ class Value {
/// @copydoc Value(bool)
explicit Value(Interval v) : Value(PrivateConstructor{}, std::move(v)) {}
/// @copydoc Value(bool)
explicit Value(Uuid v) : Value(PrivateConstructor{}, std::move(v)) {}
/// @copydoc Value(bool)
template <typename E>
explicit Value(ProtoEnum<E> v) : Value(PrivateConstructor{}, std::move(v)) {}
/// @copydoc Value(bool)
Expand Down Expand Up @@ -392,6 +396,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&);
Expand Down Expand Up @@ -466,6 +471,8 @@ 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 <typename E>
static google::spanner::v1::Type MakeTypeProto(ProtoEnum<E>) {
google::spanner::v1::Type t;
Expand Down Expand Up @@ -546,6 +553,8 @@ 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 <typename E>
static google::protobuf::Value MakeValueProto(ProtoEnum<E> e) {
return MakeValueProto(std::int64_t{E{e}});
Expand Down Expand Up @@ -638,6 +647,8 @@ class Value {
google::spanner::v1::Type const&);
static StatusOr<Interval> GetValue(Interval, google::protobuf::Value const&,
google::spanner::v1::Type const&);
static StatusOr<Uuid> GetValue(Uuid, google::protobuf::Value const&,
google::spanner::v1::Type const&);
template <typename E>
static StatusOr<ProtoEnum<E>> GetValue(ProtoEnum<E>,
google::protobuf::Value const& pv,
Expand Down
51 changes: 51 additions & 0 deletions google/cloud/spanner/value_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uuid>(5, x));
std::vector<absl::optional<Uuid>> 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<testing::Genre> " +
Expand Down Expand Up @@ -949,6 +958,20 @@ TEST(Value, ProtoConversionInterval) {
}
}

TEST(Value, ProtoConversionUuid) {
for (auto const& x : std::vector<Uuid>{
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}) {
Expand Down Expand Up @@ -1296,6 +1319,24 @@ TEST(Value, GetBadInterval) {
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
}

TEST(Value, GetBadUuid) {
Value v(Uuid{});
ClearProtoKind(v);
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));

SetProtoKind(v, google::protobuf::NULL_VALUE);
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));

SetProtoKind(v, true);
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));

SetProtoKind(v, 0.0);
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));

SetProtoKind(v, "blah");
EXPECT_THAT(v.get<Uuid>(), Not(IsOk()));
}

TEST(Value, GetBadProtoEnum) {
Value v(ProtoEnum<testing::Genre>{});
ClearProtoKind(v);
Expand Down Expand Up @@ -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>(testing::Genre::POP)),
"google.cloud.spanner.testing.POP", normal},
{Value(ProtoMessage<testing::SingerInfo>(singer)),
Expand Down Expand Up @@ -1509,6 +1551,7 @@ TEST(Value, OutputStream) {
{MakeNullValue<absl::CivilDay>(), "NULL", normal},
{MakeNullValue<Timestamp>(), "NULL", normal},
{MakeNullValue<Interval>(), "NULL", normal},
{MakeNullValue<Uuid>(), "NULL", normal},
{MakeNullValue<ProtoEnum<testing::Genre>>(), "NULL", normal},
{MakeNullValue<ProtoMessage<testing::SingerInfo>>(), "NULL", normal},

Expand All @@ -1530,6 +1573,8 @@ TEST(Value, OutputStream) {
normal},
{Value(std::vector<Timestamp>{1}), "[1970-01-01T00:00:00Z]", normal},
{Value(std::vector<Interval>{1}), "[P0D]", normal},
{Value(std::vector<Uuid>{1}), "[00000000-0000-0000-0000-000000000000]",
normal},
{Value(std::vector<ProtoEnum<testing::Genre>>{testing::JAZZ,
testing::FOLK}),
"[google.cloud.spanner.testing.JAZZ, google.cloud.spanner.testing.FOLK]",
Expand All @@ -1556,6 +1601,7 @@ TEST(Value, OutputStream) {
{MakeNullValue<std::vector<absl::CivilDay>>(), "NULL", normal},
{MakeNullValue<std::vector<Timestamp>>(), "NULL", normal},
{MakeNullValue<std::vector<Interval>>(), "NULL", normal},
{MakeNullValue<std::vector<Uuid>>(), "NULL", normal},
{MakeNullValue<std::vector<ProtoEnum<testing::Genre>>>(), "NULL", normal},
{MakeNullValue<std::vector<ProtoMessage<testing::SingerInfo>>>(), "NULL",
normal},
Expand Down Expand Up @@ -1710,6 +1756,11 @@ TEST(Value, OutputStreamMatchesT) {
StreamMatchesValueStream(Interval());
StreamMatchesValueStream(MakeInterval("P1Y2M3DT4H5M6.789S").value());

// Uuid
StreamMatchesValueStream(Uuid());
StreamMatchesValueStream(
MakeUuid("{0b6ed04ca16dfc4652817f9978c13738}").value());

// ProtoEnum
StreamMatchesValueStream(ProtoEnum<testing::Genre>());
StreamMatchesValueStream(ProtoEnum<testing::Genre>(testing::ROCK));
Expand Down