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
25 changes: 25 additions & 0 deletions google/cloud/spanner/value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
case google::spanner::v1::TypeCode::JSON:
case google::spanner::v1::TypeCode::TIMESTAMP:
case google::spanner::v1::TypeCode::NUMERIC:
case google::spanner::v1::TypeCode::INTERVAL:
return os << v.string_value();

case google::spanner::v1::TypeCode::DATE:
Expand Down Expand Up @@ -269,6 +270,10 @@ bool Value::TypeProtoIs(absl::CivilDay, google::spanner::v1::Type const& type) {
return type.code() == google::spanner::v1::TypeCode::DATE;
}

bool Value::TypeProtoIs(Interval, google::spanner::v1::Type const& type) {
return type.code() == google::spanner::v1::TypeCode::INTERVAL;
}

bool Value::TypeProtoIs(std::string const&,
google::spanner::v1::Type const& type) {
return type.code() == google::spanner::v1::TypeCode::STRING;
Expand Down Expand Up @@ -404,6 +409,12 @@ google::spanner::v1::Type Value::MakeTypeProto(absl::CivilDay) {
return t;
}

google::spanner::v1::Type Value::MakeTypeProto(Interval) {
google::spanner::v1::Type t;
t.set_code(google::spanner::v1::TypeCode::INTERVAL);
return t;
}

google::spanner::v1::Type Value::MakeTypeProto(int) {
return MakeTypeProto(std::int64_t{});
}
Expand Down Expand Up @@ -520,6 +531,12 @@ google::protobuf::Value Value::MakeValueProto(absl::CivilDay d) {
return v;
}

google::protobuf::Value Value::MakeValueProto(Interval intvl) {
google::protobuf::Value v;
v.set_string_value(std::string(intvl));
return v;
}

google::protobuf::Value Value::MakeValueProto(int i) {
return MakeValueProto(std::int64_t{i});
}
Expand Down Expand Up @@ -711,6 +728,14 @@ StatusOr<absl::CivilDay> Value::GetValue(absl::CivilDay,
s + ": Failed to match RFC3339 full-date", GCP_ERROR_INFO());
}

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 MakeInterval(pv.string_value());
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace spanner
} // namespace cloud
Expand Down
9 changes: 9 additions & 0 deletions google/cloud/spanner/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "google/cloud/spanner/bytes.h"
#include "google/cloud/spanner/date.h"
#include "google/cloud/spanner/internal/tuple_utils.h"
#include "google/cloud/spanner/interval.h"
#include "google/cloud/spanner/json.h"
#include "google/cloud/spanner/numeric.h"
#include "google/cloud/spanner/oid.h"
Expand Down Expand Up @@ -77,6 +78,7 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
* OID(PG) | `google::cloud::spanner::PgOid`
* TIMESTAMP | `google::cloud::spanner::Timestamp`
* DATE | `absl::CivilDay`
* INTERVAL | `google::cloud::spanner::Interval`
* ENUM | `google::cloud::spanner::ProtoEnum<E>`
* PROTO | `google::cloud::spanner::ProtoMessage<M>`
* ARRAY | `std::vector<T>` // [1]
Expand Down Expand Up @@ -222,6 +224,8 @@ class Value {
explicit Value(absl::CivilDay v)
: Value(PrivateConstructor{}, std::move(v)) {}
/// @copydoc Value(bool)
explicit Value(Interval 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 @@ -387,6 +391,7 @@ class Value {
static bool TypeProtoIs(Timestamp, google::spanner::v1::Type const&);
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(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 @@ -460,6 +465,7 @@ class Value {
static google::spanner::v1::Type MakeTypeProto(Timestamp);
static google::spanner::v1::Type MakeTypeProto(CommitTimestamp);
static google::spanner::v1::Type MakeTypeProto(absl::CivilDay);
static google::spanner::v1::Type MakeTypeProto(Interval);
template <typename E>
static google::spanner::v1::Type MakeTypeProto(ProtoEnum<E>) {
google::spanner::v1::Type t;
Expand Down Expand Up @@ -539,6 +545,7 @@ class Value {
static google::protobuf::Value MakeValueProto(Timestamp ts);
static google::protobuf::Value MakeValueProto(CommitTimestamp ts);
static google::protobuf::Value MakeValueProto(absl::CivilDay d);
static google::protobuf::Value MakeValueProto(Interval intvl);
template <typename E>
static google::protobuf::Value MakeValueProto(ProtoEnum<E> e) {
return MakeValueProto(std::int64_t{E{e}});
Expand Down Expand Up @@ -629,6 +636,8 @@ class Value {
static StatusOr<absl::CivilDay> GetValue(absl::CivilDay,
google::protobuf::Value const&,
google::spanner::v1::Type const&);
static StatusOr<Interval> GetValue(Interval, 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
53 changes: 53 additions & 0 deletions google/cloud/spanner/value_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,15 @@ TEST(Value, BasicSemantics) {
v.resize(10);
TestBasicSemantics(v);

for (auto x : {Interval(), MakeInterval("P1Y1M1D").value()}) {
SCOPED_TRACE("Testing: google::cloud::spanner::Interval " + std::string{x});
TestBasicSemantics(x);
TestBasicSemantics(std::vector<Interval>(5, x));
std::vector<absl::optional<Interval>> 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 @@ -922,6 +931,24 @@ TEST(Value, ProtoConversionDate) {
}
}

TEST(Value, ProtoConversionInterval) {
for (auto const& x : std::vector<Interval>{
MakeInterval("-P178000Y").value(),
MakeInterval("-P1Y2M3DT4H5M6.789S").value(),
MakeInterval("-PT0.000001S").value(),
Interval(),
MakeInterval("PT0.000001S").value(),
MakeInterval("P1Y2M3DT4H5M6.789S").value(),
MakeInterval("P178000Y").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::INTERVAL, 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 @@ -1251,6 +1278,24 @@ TEST(Value, GetBadDate) {
EXPECT_THAT(v.get<absl::CivilDay>(), Not(IsOk()));
}

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

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

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

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

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

TEST(Value, GetBadProtoEnum) {
Value v(ProtoEnum<testing::Genre>{});
ClearProtoKind(v);
Expand Down Expand Up @@ -1429,6 +1474,7 @@ TEST(Value, OutputStream) {
{Value(PgOid(1234567890)), "1234567890", normal},
{Value(absl::CivilDay()), "1970-01-01", normal},
{Value(Timestamp()), "1970-01-01T00:00:00Z", normal},
{Value(Interval()), "P0D", normal},
{Value(ProtoEnum<testing::Genre>(testing::Genre::POP)),
"google.cloud.spanner.testing.POP", normal},
{Value(ProtoMessage<testing::SingerInfo>(singer)),
Expand Down Expand Up @@ -1462,6 +1508,7 @@ TEST(Value, OutputStream) {
{MakeNullValue<PgOid>(), "NULL", normal},
{MakeNullValue<absl::CivilDay>(), "NULL", normal},
{MakeNullValue<Timestamp>(), "NULL", normal},
{MakeNullValue<Interval>(), "NULL", normal},
{MakeNullValue<ProtoEnum<testing::Genre>>(), "NULL", normal},
{MakeNullValue<ProtoMessage<testing::SingerInfo>>(), "NULL", normal},

Expand All @@ -1482,6 +1529,7 @@ TEST(Value, OutputStream) {
{Value(std::vector<absl::CivilDay>{2}), "[1970-01-01, 1970-01-01]",
normal},
{Value(std::vector<Timestamp>{1}), "[1970-01-01T00:00:00Z]", normal},
{Value(std::vector<Interval>{1}), "[P0D]", normal},
{Value(std::vector<ProtoEnum<testing::Genre>>{testing::JAZZ,
testing::FOLK}),
"[google.cloud.spanner.testing.JAZZ, google.cloud.spanner.testing.FOLK]",
Expand All @@ -1507,6 +1555,7 @@ TEST(Value, OutputStream) {
{MakeNullValue<std::vector<Numeric>>(), "NULL", normal},
{MakeNullValue<std::vector<absl::CivilDay>>(), "NULL", normal},
{MakeNullValue<std::vector<Timestamp>>(), "NULL", normal},
{MakeNullValue<std::vector<Interval>>(), "NULL", normal},
{MakeNullValue<std::vector<ProtoEnum<testing::Genre>>>(), "NULL", normal},
{MakeNullValue<std::vector<ProtoMessage<testing::SingerInfo>>>(), "NULL",
normal},
Expand Down Expand Up @@ -1657,6 +1706,10 @@ TEST(Value, OutputStreamMatchesT) {
StreamMatchesValueStream(Timestamp());
StreamMatchesValueStream(MakeTimestamp(MakeTime(1, 1)).value());

// Interval
StreamMatchesValueStream(Interval());
StreamMatchesValueStream(MakeInterval("P1Y2M3DT4H5M6.789S").value());

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