Skip to content

Commit 6b44994

Browse files
authored
feat(spanner): add spanner::Value support for ProtoEnum/ProtoMessage (#13747)
1 parent 924bb63 commit 6b44994

File tree

3 files changed

+270
-0
lines changed

3 files changed

+270
-0
lines changed

google/cloud/spanner/value.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
// limitations under the License.
1414

1515
#include "google/cloud/spanner/value.h"
16+
#include "google/cloud/internal/base64_transforms.h"
1617
#include "google/cloud/internal/strerror.h"
1718
#include "absl/time/civil_time.h"
19+
#include <google/protobuf/descriptor.h>
20+
#include <google/protobuf/message.h>
1821
#include <cerrno>
1922
#include <cmath>
2023
#include <cstdlib>
@@ -58,6 +61,10 @@ bool Equal(google::spanner::v1::Type const& pt1, // NOLINT(misc-no-recursion)
5861
case google::spanner::v1::TypeCode::NUMERIC:
5962
if (pt1.type_annotation() != pt2.type_annotation()) return false;
6063
return pv1.string_value() == pv2.string_value();
64+
case google::spanner::v1::TypeCode::PROTO:
65+
case google::spanner::v1::TypeCode::ENUM:
66+
if (pt1.proto_type_fqn() != pt2.proto_type_fqn()) return false;
67+
return pv1.string_value() == pv2.string_value();
6168
case google::spanner::v1::TypeCode::ARRAY: {
6269
auto const& etype1 = pt1.array_element_type();
6370
auto const& etype2 = pt2.array_element_type();
@@ -152,6 +159,36 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
152159
return os
153160
<< spanner_internal::FromProto(t, v).get<absl::CivilDay>().value();
154161

162+
case google::spanner::v1::TypeCode::ENUM:
163+
if (auto const* p = google::protobuf::DescriptorPool::generated_pool()) {
164+
if (auto const* d = p->FindEnumTypeByName(t.proto_type_fqn())) {
165+
auto number = std::stoi(v.string_value());
166+
if (std::to_string(number) == v.string_value()) {
167+
if (auto const* vd = d->FindValueByNumber(number)) {
168+
return os << vd->full_name();
169+
}
170+
}
171+
}
172+
}
173+
return os << t.proto_type_fqn() << ".{" << v.string_value() << "}";
174+
175+
case google::spanner::v1::TypeCode::PROTO:
176+
if (auto const* p = google::protobuf::DescriptorPool::generated_pool()) {
177+
if (auto const* d = p->FindMessageTypeByName(t.proto_type_fqn())) {
178+
if (auto bytes = internal::Base64DecodeToBytes(v.string_value())) {
179+
auto* f = google::protobuf::MessageFactory::generated_factory();
180+
if (auto const* pt = f->GetPrototype(d)) {
181+
std::unique_ptr<google::protobuf::Message> m(pt->New());
182+
m->ParseFromString(std::string(bytes->begin(), bytes->end()));
183+
auto s = m->ShortDebugString();
184+
return os << t.proto_type_fqn() << " { " << s
185+
<< (s.empty() ? "" : " ") << "}";
186+
}
187+
}
188+
}
189+
}
190+
return os << t.proto_type_fqn() << " { <unknown> }";
191+
155192
case google::spanner::v1::TypeCode::ARRAY: {
156193
char const* delimiter = "";
157194
os << '[';

google/cloud/spanner/value.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
#include "google/cloud/spanner/json.h"
2222
#include "google/cloud/spanner/numeric.h"
2323
#include "google/cloud/spanner/oid.h"
24+
#include "google/cloud/spanner/proto_enum.h"
25+
#include "google/cloud/spanner/proto_message.h"
2426
#include "google/cloud/spanner/timestamp.h"
2527
#include "google/cloud/spanner/version.h"
28+
#include "google/cloud/internal/base64_transforms.h"
2629
#include "google/cloud/internal/throw_delegate.h"
2730
#include "google/cloud/optional.h"
2831
#include "google/cloud/status_or.h"
@@ -72,6 +75,8 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
7275
* OID(PG) | `google::cloud::spanner::PgOid`
7376
* TIMESTAMP | `google::cloud::spanner::Timestamp`
7477
* DATE | `absl::CivilDay`
78+
* ENUM | `google::cloud::spanner::ProtoEnum<E>`
79+
* PROTO | `google::cloud::spanner::ProtoMessage<M>`
7580
* ARRAY | `std::vector<T>` // [1]
7681
* STRUCT | `std::tuple<Ts...>`
7782
*
@@ -212,6 +217,13 @@ class Value {
212217
/// @copydoc Value(bool)
213218
explicit Value(absl::CivilDay v)
214219
: Value(PrivateConstructor{}, std::move(v)) {}
220+
/// @copydoc Value(bool)
221+
template <typename E>
222+
explicit Value(ProtoEnum<E> v) : Value(PrivateConstructor{}, std::move(v)) {}
223+
/// @copydoc Value(bool)
224+
template <typename M>
225+
explicit Value(ProtoMessage<M> v)
226+
: Value(PrivateConstructor{}, std::move(v)) {}
215227

216228
/**
217229
* Constructs an instance from common C++ literal types that closely, though
@@ -377,6 +389,18 @@ class Value {
377389
static bool TypeProtoIs(Numeric const&, google::spanner::v1::Type const&);
378390
static bool TypeProtoIs(PgNumeric const&, google::spanner::v1::Type const&);
379391
static bool TypeProtoIs(PgOid const&, google::spanner::v1::Type const&);
392+
template <typename E>
393+
static bool TypeProtoIs(ProtoEnum<E> const&,
394+
google::spanner::v1::Type const& type) {
395+
return type.code() == google::spanner::v1::TypeCode::ENUM &&
396+
type.proto_type_fqn() == ProtoEnum<E>::TypeName();
397+
}
398+
template <typename M>
399+
static bool TypeProtoIs(ProtoMessage<M> const&,
400+
google::spanner::v1::Type const& type) {
401+
return type.code() == google::spanner::v1::TypeCode::PROTO &&
402+
type.proto_type_fqn() == ProtoMessage<M>::TypeName();
403+
}
380404
template <typename T>
381405
static bool TypeProtoIs(absl::optional<T>,
382406
google::spanner::v1::Type const& type) {
@@ -430,6 +454,20 @@ class Value {
430454
static google::spanner::v1::Type MakeTypeProto(Timestamp);
431455
static google::spanner::v1::Type MakeTypeProto(CommitTimestamp);
432456
static google::spanner::v1::Type MakeTypeProto(absl::CivilDay);
457+
template <typename E>
458+
static google::spanner::v1::Type MakeTypeProto(ProtoEnum<E>) {
459+
google::spanner::v1::Type t;
460+
t.set_code(google::spanner::v1::TypeCode::ENUM);
461+
t.set_proto_type_fqn(ProtoEnum<E>::TypeName());
462+
return t;
463+
}
464+
template <typename M>
465+
static google::spanner::v1::Type MakeTypeProto(ProtoMessage<M>) {
466+
google::spanner::v1::Type t;
467+
t.set_code(google::spanner::v1::TypeCode::PROTO);
468+
t.set_proto_type_fqn(ProtoMessage<M>::TypeName());
469+
return t;
470+
}
433471
static google::spanner::v1::Type MakeTypeProto(int);
434472
static google::spanner::v1::Type MakeTypeProto(char const*);
435473
template <typename T>
@@ -494,6 +532,16 @@ class Value {
494532
static google::protobuf::Value MakeValueProto(Timestamp ts);
495533
static google::protobuf::Value MakeValueProto(CommitTimestamp ts);
496534
static google::protobuf::Value MakeValueProto(absl::CivilDay d);
535+
template <typename E>
536+
static google::protobuf::Value MakeValueProto(ProtoEnum<E> e) {
537+
return MakeValueProto(std::int64_t{E{e}});
538+
}
539+
template <typename M>
540+
static google::protobuf::Value MakeValueProto(ProtoMessage<M> m) {
541+
internal::Base64Encoder encoder;
542+
for (auto c : std::string{m}) encoder.PushBack(c);
543+
return MakeValueProto(std::move(encoder).FlushAndPad());
544+
}
497545
static google::protobuf::Value MakeValueProto(int i);
498546
static google::protobuf::Value MakeValueProto(char const* s);
499547
template <typename T>
@@ -572,6 +620,32 @@ class Value {
572620
static StatusOr<absl::CivilDay> GetValue(absl::CivilDay,
573621
google::protobuf::Value const&,
574622
google::spanner::v1::Type const&);
623+
template <typename E>
624+
static StatusOr<ProtoEnum<E>> GetValue(ProtoEnum<E>,
625+
google::protobuf::Value const& pv,
626+
google::spanner::v1::Type const& pt) {
627+
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
628+
return Status(StatusCode::kUnknown, "missing ENUM");
629+
}
630+
auto value = GetValue(std::int64_t{}, pv, pt);
631+
if (!value) return std::move(value).status();
632+
if (static_cast<typename std::underlying_type_t<E>>(*value) != *value) {
633+
return Status(StatusCode::kUnknown,
634+
"Value out of range: " + pv.string_value());
635+
}
636+
return ProtoEnum<E>(static_cast<E>(*std::move(value)));
637+
}
638+
template <typename M>
639+
static StatusOr<ProtoMessage<M>> GetValue(ProtoMessage<M>,
640+
google::protobuf::Value const& pv,
641+
google::spanner::v1::Type const&) {
642+
if (pv.kind_case() != google::protobuf::Value::kStringValue) {
643+
return Status(StatusCode::kUnknown, "missing PROTO");
644+
}
645+
auto bytes = internal::Base64DecodeToBytes(pv.string_value());
646+
if (!bytes) return std::move(bytes).status();
647+
return ProtoMessage<M>(std::string(bytes->begin(), bytes->end()));
648+
}
575649
template <typename T, typename V>
576650
static StatusOr<absl::optional<T>> GetValue(
577651
absl::optional<T> const&, V&& pv, google::spanner::v1::Type const& pt) {

0 commit comments

Comments
 (0)