Skip to content

Commit 440dcd6

Browse files
authored
feat(spanner): add spanner::Value support for TypeCode::INTERVAL (#15094)
1 parent e8eb903 commit 440dcd6

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

google/cloud/spanner/value.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
158158
case google::spanner::v1::TypeCode::JSON:
159159
case google::spanner::v1::TypeCode::TIMESTAMP:
160160
case google::spanner::v1::TypeCode::NUMERIC:
161+
case google::spanner::v1::TypeCode::INTERVAL:
161162
return os << v.string_value();
162163

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

273+
bool Value::TypeProtoIs(Interval, google::spanner::v1::Type const& type) {
274+
return type.code() == google::spanner::v1::TypeCode::INTERVAL;
275+
}
276+
272277
bool Value::TypeProtoIs(std::string const&,
273278
google::spanner::v1::Type const& type) {
274279
return type.code() == google::spanner::v1::TypeCode::STRING;
@@ -404,6 +409,12 @@ google::spanner::v1::Type Value::MakeTypeProto(absl::CivilDay) {
404409
return t;
405410
}
406411

412+
google::spanner::v1::Type Value::MakeTypeProto(Interval) {
413+
google::spanner::v1::Type t;
414+
t.set_code(google::spanner::v1::TypeCode::INTERVAL);
415+
return t;
416+
}
417+
407418
google::spanner::v1::Type Value::MakeTypeProto(int) {
408419
return MakeTypeProto(std::int64_t{});
409420
}
@@ -520,6 +531,12 @@ google::protobuf::Value Value::MakeValueProto(absl::CivilDay d) {
520531
return v;
521532
}
522533

534+
google::protobuf::Value Value::MakeValueProto(Interval intvl) {
535+
google::protobuf::Value v;
536+
v.set_string_value(std::string(intvl));
537+
return v;
538+
}
539+
523540
google::protobuf::Value Value::MakeValueProto(int i) {
524541
return MakeValueProto(std::int64_t{i});
525542
}
@@ -711,6 +728,14 @@ StatusOr<absl::CivilDay> Value::GetValue(absl::CivilDay,
711728
s + ": Failed to match RFC3339 full-date", GCP_ERROR_INFO());
712729
}
713730

731+
StatusOr<Interval> Value::GetValue(Interval, google::protobuf::Value const& pv,
732+
google::spanner::v1::Type const&) {
733+
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
734+
return Status(StatusCode::kUnknown, "missing Interval");
735+
}
736+
return MakeInterval(pv.string_value());
737+
}
738+
714739
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
715740
} // namespace spanner
716741
} // namespace cloud

google/cloud/spanner/value.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "google/cloud/spanner/bytes.h"
1919
#include "google/cloud/spanner/date.h"
2020
#include "google/cloud/spanner/internal/tuple_utils.h"
21+
#include "google/cloud/spanner/interval.h"
2122
#include "google/cloud/spanner/json.h"
2223
#include "google/cloud/spanner/numeric.h"
2324
#include "google/cloud/spanner/oid.h"
@@ -77,6 +78,7 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
7778
* OID(PG) | `google::cloud::spanner::PgOid`
7879
* TIMESTAMP | `google::cloud::spanner::Timestamp`
7980
* DATE | `absl::CivilDay`
81+
* INTERVAL | `google::cloud::spanner::Interval`
8082
* ENUM | `google::cloud::spanner::ProtoEnum<E>`
8183
* PROTO | `google::cloud::spanner::ProtoMessage<M>`
8284
* ARRAY | `std::vector<T>` // [1]
@@ -222,6 +224,8 @@ class Value {
222224
explicit Value(absl::CivilDay v)
223225
: Value(PrivateConstructor{}, std::move(v)) {}
224226
/// @copydoc Value(bool)
227+
explicit Value(Interval v) : Value(PrivateConstructor{}, std::move(v)) {}
228+
/// @copydoc Value(bool)
225229
template <typename E>
226230
explicit Value(ProtoEnum<E> v) : Value(PrivateConstructor{}, std::move(v)) {}
227231
/// @copydoc Value(bool)
@@ -387,6 +391,7 @@ class Value {
387391
static bool TypeProtoIs(Timestamp, google::spanner::v1::Type const&);
388392
static bool TypeProtoIs(CommitTimestamp, google::spanner::v1::Type const&);
389393
static bool TypeProtoIs(absl::CivilDay, google::spanner::v1::Type const&);
394+
static bool TypeProtoIs(Interval, google::spanner::v1::Type const&);
390395
static bool TypeProtoIs(std::string const&, google::spanner::v1::Type const&);
391396
static bool TypeProtoIs(Bytes const&, google::spanner::v1::Type const&);
392397
static bool TypeProtoIs(Json const&, google::spanner::v1::Type const&);
@@ -460,6 +465,7 @@ class Value {
460465
static google::spanner::v1::Type MakeTypeProto(Timestamp);
461466
static google::spanner::v1::Type MakeTypeProto(CommitTimestamp);
462467
static google::spanner::v1::Type MakeTypeProto(absl::CivilDay);
468+
static google::spanner::v1::Type MakeTypeProto(Interval);
463469
template <typename E>
464470
static google::spanner::v1::Type MakeTypeProto(ProtoEnum<E>) {
465471
google::spanner::v1::Type t;
@@ -539,6 +545,7 @@ class Value {
539545
static google::protobuf::Value MakeValueProto(Timestamp ts);
540546
static google::protobuf::Value MakeValueProto(CommitTimestamp ts);
541547
static google::protobuf::Value MakeValueProto(absl::CivilDay d);
548+
static google::protobuf::Value MakeValueProto(Interval intvl);
542549
template <typename E>
543550
static google::protobuf::Value MakeValueProto(ProtoEnum<E> e) {
544551
return MakeValueProto(std::int64_t{E{e}});
@@ -629,6 +636,8 @@ class Value {
629636
static StatusOr<absl::CivilDay> GetValue(absl::CivilDay,
630637
google::protobuf::Value const&,
631638
google::spanner::v1::Type const&);
639+
static StatusOr<Interval> GetValue(Interval, google::protobuf::Value const&,
640+
google::spanner::v1::Type const&);
632641
template <typename E>
633642
static StatusOr<ProtoEnum<E>> GetValue(ProtoEnum<E>,
634643
google::protobuf::Value const& pv,

google/cloud/spanner/value_test.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,15 @@ TEST(Value, BasicSemantics) {
264264
v.resize(10);
265265
TestBasicSemantics(v);
266266

267+
for (auto x : {Interval(), MakeInterval("P1Y1M1D").value()}) {
268+
SCOPED_TRACE("Testing: google::cloud::spanner::Interval " + std::string{x});
269+
TestBasicSemantics(x);
270+
TestBasicSemantics(std::vector<Interval>(5, x));
271+
std::vector<absl::optional<Interval>> v(5, x);
272+
v.resize(10);
273+
TestBasicSemantics(v);
274+
}
275+
267276
for (auto x : {testing::Genre::POP, testing::Genre::JAZZ,
268277
testing::Genre::FOLK, testing::Genre::ROCK}) {
269278
SCOPED_TRACE("Testing: ProtoEnum<testing::Genre> " +
@@ -922,6 +931,24 @@ TEST(Value, ProtoConversionDate) {
922931
}
923932
}
924933

934+
TEST(Value, ProtoConversionInterval) {
935+
for (auto const& x : std::vector<Interval>{
936+
MakeInterval("-P178000Y").value(),
937+
MakeInterval("-P1Y2M3DT4H5M6.789S").value(),
938+
MakeInterval("-PT0.000001S").value(),
939+
Interval(),
940+
MakeInterval("PT0.000001S").value(),
941+
MakeInterval("P1Y2M3DT4H5M6.789S").value(),
942+
MakeInterval("P178000Y").value(),
943+
}) {
944+
Value const v(x);
945+
auto const p = spanner_internal::ToProto(v);
946+
EXPECT_EQ(v, spanner_internal::FromProto(p.first, p.second));
947+
EXPECT_EQ(google::spanner::v1::TypeCode::INTERVAL, p.first.code());
948+
EXPECT_EQ(std::string{x}, p.second.string_value());
949+
}
950+
}
951+
925952
TEST(Value, ProtoConversionProtoEnum) {
926953
for (auto e : {testing::Genre::POP, testing::Genre::JAZZ,
927954
testing::Genre::FOLK, testing::Genre::ROCK}) {
@@ -1251,6 +1278,24 @@ TEST(Value, GetBadDate) {
12511278
EXPECT_THAT(v.get<absl::CivilDay>(), Not(IsOk()));
12521279
}
12531280

1281+
TEST(Value, GetBadInterval) {
1282+
Value v(Interval{});
1283+
ClearProtoKind(v);
1284+
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
1285+
1286+
SetProtoKind(v, google::protobuf::NULL_VALUE);
1287+
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
1288+
1289+
SetProtoKind(v, true);
1290+
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
1291+
1292+
SetProtoKind(v, 0.0);
1293+
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
1294+
1295+
SetProtoKind(v, "blah");
1296+
EXPECT_THAT(v.get<Interval>(), Not(IsOk()));
1297+
}
1298+
12541299
TEST(Value, GetBadProtoEnum) {
12551300
Value v(ProtoEnum<testing::Genre>{});
12561301
ClearProtoKind(v);
@@ -1429,6 +1474,7 @@ TEST(Value, OutputStream) {
14291474
{Value(PgOid(1234567890)), "1234567890", normal},
14301475
{Value(absl::CivilDay()), "1970-01-01", normal},
14311476
{Value(Timestamp()), "1970-01-01T00:00:00Z", normal},
1477+
{Value(Interval()), "P0D", normal},
14321478
{Value(ProtoEnum<testing::Genre>(testing::Genre::POP)),
14331479
"google.cloud.spanner.testing.POP", normal},
14341480
{Value(ProtoMessage<testing::SingerInfo>(singer)),
@@ -1462,6 +1508,7 @@ TEST(Value, OutputStream) {
14621508
{MakeNullValue<PgOid>(), "NULL", normal},
14631509
{MakeNullValue<absl::CivilDay>(), "NULL", normal},
14641510
{MakeNullValue<Timestamp>(), "NULL", normal},
1511+
{MakeNullValue<Interval>(), "NULL", normal},
14651512
{MakeNullValue<ProtoEnum<testing::Genre>>(), "NULL", normal},
14661513
{MakeNullValue<ProtoMessage<testing::SingerInfo>>(), "NULL", normal},
14671514

@@ -1482,6 +1529,7 @@ TEST(Value, OutputStream) {
14821529
{Value(std::vector<absl::CivilDay>{2}), "[1970-01-01, 1970-01-01]",
14831530
normal},
14841531
{Value(std::vector<Timestamp>{1}), "[1970-01-01T00:00:00Z]", normal},
1532+
{Value(std::vector<Interval>{1}), "[P0D]", normal},
14851533
{Value(std::vector<ProtoEnum<testing::Genre>>{testing::JAZZ,
14861534
testing::FOLK}),
14871535
"[google.cloud.spanner.testing.JAZZ, google.cloud.spanner.testing.FOLK]",
@@ -1507,6 +1555,7 @@ TEST(Value, OutputStream) {
15071555
{MakeNullValue<std::vector<Numeric>>(), "NULL", normal},
15081556
{MakeNullValue<std::vector<absl::CivilDay>>(), "NULL", normal},
15091557
{MakeNullValue<std::vector<Timestamp>>(), "NULL", normal},
1558+
{MakeNullValue<std::vector<Interval>>(), "NULL", normal},
15101559
{MakeNullValue<std::vector<ProtoEnum<testing::Genre>>>(), "NULL", normal},
15111560
{MakeNullValue<std::vector<ProtoMessage<testing::SingerInfo>>>(), "NULL",
15121561
normal},
@@ -1657,6 +1706,10 @@ TEST(Value, OutputStreamMatchesT) {
16571706
StreamMatchesValueStream(Timestamp());
16581707
StreamMatchesValueStream(MakeTimestamp(MakeTime(1, 1)).value());
16591708

1709+
// Interval
1710+
StreamMatchesValueStream(Interval());
1711+
StreamMatchesValueStream(MakeInterval("P1Y2M3DT4H5M6.789S").value());
1712+
16601713
// ProtoEnum
16611714
StreamMatchesValueStream(ProtoEnum<testing::Genre>());
16621715
StreamMatchesValueStream(ProtoEnum<testing::Genre>(testing::ROCK));

0 commit comments

Comments
 (0)