Google Cloud Spanner C++ Client
A C++ Client Library for Google Cloud Spanner
value.h
Go to the documentation of this file.
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_VALUE_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_VALUE_H
17 
20 #include "google/cloud/spanner/internal/tuple_utils.h"
23 #include "google/cloud/internal/throw_delegate.h"
24 #include "google/cloud/optional.h"
25 #include "google/cloud/status_or.h"
26 #include <google/protobuf/struct.pb.h>
27 #include <google/protobuf/util/message_differencer.h>
28 #include <google/spanner/v1/type.pb.h>
29 #include <ostream>
30 #include <string>
31 #include <tuple>
32 #include <type_traits>
33 #include <utility>
34 
35 namespace google {
36 namespace cloud {
37 namespace spanner {
38 inline namespace SPANNER_CLIENT_NS {
39 
40 class Value; // Defined later in this file.
41 
42 // Internal implementation details that callers should not use.
43 namespace internal {
44 Value FromProto(google::spanner::v1::Type t, google::protobuf::Value v);
45 std::pair<google::spanner::v1::Type, google::protobuf::Value> ToProto(Value v);
46 } // namespace internal
47 
162 class Value {
163  public:
169  Value() = default;
170 
171  // Copy and move.
172  Value(Value const&) = default;
173  Value(Value&&) = default;
174  Value& operator=(Value const&) = default;
175  Value& operator=(Value&&) = default;
176 
178  explicit Value(bool v) : Value(PrivateConstructor{}, v) {}
180  explicit Value(std::int64_t v) : Value(PrivateConstructor{}, v) {}
182  explicit Value(double v) : Value(PrivateConstructor{}, v) {}
184  explicit Value(std::string v) : Value(PrivateConstructor{}, std::move(v)) {}
186  explicit Value(Bytes v) : Value(PrivateConstructor{}, std::move(v)) {}
188  explicit Value(Timestamp v) : Value(PrivateConstructor{}, std::move(v)) {}
190  explicit Value(CommitTimestamp v)
191  : Value(PrivateConstructor{}, std::move(v)) {}
193  explicit Value(Date v) : Value(PrivateConstructor{}, std::move(v)) {}
194 
212  explicit Value(int v) : Value(PrivateConstructor{}, v) {}
214  explicit Value(char const* v) : Value(PrivateConstructor{}, v) {}
215 
220  template <typename T>
221  explicit Value(optional<T> opt)
222  : Value(PrivateConstructor{}, std::move(opt)) {}
223 
236  template <typename T>
237  explicit Value(std::vector<T> v) : Value(PrivateConstructor{}, std::move(v)) {
238  static_assert(!IsVector<typename std::decay<T>::type>::value,
239  "vector of vector not allowed. See value.h documentation.");
240  }
241 
249  template <typename... Ts>
250  explicit Value(std::tuple<Ts...> tup)
251  : Value(PrivateConstructor{}, std::move(tup)) {}
252 
253  friend bool operator==(Value const& a, Value const& b);
254  friend bool operator!=(Value const& a, Value const& b) { return !(a == b); }
255 
285  template <typename T>
286  StatusOr<T> get() const& {
287  if (!TypeProtoIs(T{}, type_))
288  return Status(StatusCode::kUnknown, "wrong type");
289  if (value_.kind_case() == google::protobuf::Value::kNullValue) {
290  if (IsOptional<T>::value) return T{};
291  return Status(StatusCode::kUnknown, "null value");
292  }
293  return GetValue(T{}, value_, type_);
294  }
295 
297  template <typename T>
298  StatusOr<T> get() && {
299  if (!TypeProtoIs(T{}, type_))
300  return Status(StatusCode::kUnknown, "wrong type");
301  if (value_.kind_case() == google::protobuf::Value::kNullValue) {
302  if (IsOptional<T>::value) return T{};
303  return Status(StatusCode::kUnknown, "null value");
304  }
305  auto tag = T{}; // Works around an odd msvc issue
306  return GetValue(std::move(tag), std::move(value_), type_);
307  }
308 
321  friend std::ostream& operator<<(std::ostream& os, Value const& v);
322 
329  friend void PrintTo(Value const& v, std::ostream* os) { *os << v; }
330 
331  private:
332  // Metafunction that returns true if `T` is an optional<U>
333  template <typename T>
334  struct IsOptional : std::false_type {};
335  template <typename T>
336  struct IsOptional<optional<T>> : std::true_type {};
337 
338  // Metafunction that returns true if `T` is a std::vector<U>
339  template <typename T>
340  struct IsVector : std::false_type {};
341  template <typename... Ts>
342  struct IsVector<std::vector<Ts...>> : std::true_type {};
343 
344  // Tag-dispatch overloads to check if a C++ type matches the type specified
345  // by the given `Type` proto.
346  static bool TypeProtoIs(bool, google::spanner::v1::Type const&);
347  static bool TypeProtoIs(std::int64_t, google::spanner::v1::Type const&);
348  static bool TypeProtoIs(double, google::spanner::v1::Type const&);
349  static bool TypeProtoIs(Timestamp, google::spanner::v1::Type const&);
350  static bool TypeProtoIs(CommitTimestamp, google::spanner::v1::Type const&);
351  static bool TypeProtoIs(Date, google::spanner::v1::Type const&);
352  static bool TypeProtoIs(std::string const&, google::spanner::v1::Type const&);
353  static bool TypeProtoIs(Bytes const&, google::spanner::v1::Type const&);
354  template <typename T>
355  static bool TypeProtoIs(optional<T>, google::spanner::v1::Type const& type) {
356  return TypeProtoIs(T{}, type);
357  }
358  template <typename T>
359  static bool TypeProtoIs(std::vector<T> const&,
360  google::spanner::v1::Type const& type) {
361  return type.code() == google::spanner::v1::TypeCode::ARRAY &&
362  TypeProtoIs(T{}, type.array_element_type());
363  }
364  template <typename... Ts>
365  static bool TypeProtoIs(std::tuple<Ts...> const& tup,
366  google::spanner::v1::Type const& type) {
367  bool ok = type.code() == google::spanner::v1::TypeCode::STRUCT;
368  ok = ok && type.struct_type().fields().size() == sizeof...(Ts);
369  internal::ForEach(tup, IsStructTypeProto{ok, 0}, type.struct_type());
370  return ok;
371  }
372 
373  // A functor to be used with internal::ForEach to check if a StructType proto
374  // matches the types in a std::tuple.
375  struct IsStructTypeProto {
376  bool& ok;
377  int field;
378  template <typename T>
379  void operator()(T const&, google::spanner::v1::StructType const& type) {
380  ok = ok && TypeProtoIs(T{}, type.fields(field).type());
381  ++field;
382  }
383  template <typename T>
384  void operator()(std::pair<std::string, T> const&,
385  google::spanner::v1::StructType const& type) {
386  operator()(T{}, type);
387  }
388  };
389 
390  // Tag-dispatch overloads to convert a C++ type to a `Type` protobuf. The
391  // argument type is the tag, the argument value is ignored.
392  static google::spanner::v1::Type MakeTypeProto(bool);
393  static google::spanner::v1::Type MakeTypeProto(std::int64_t);
394  static google::spanner::v1::Type MakeTypeProto(double);
395  static google::spanner::v1::Type MakeTypeProto(std::string const&);
396  static google::spanner::v1::Type MakeTypeProto(Bytes const&);
397  static google::spanner::v1::Type MakeTypeProto(Timestamp);
398  static google::spanner::v1::Type MakeTypeProto(CommitTimestamp);
399  static google::spanner::v1::Type MakeTypeProto(Date);
400  static google::spanner::v1::Type MakeTypeProto(int);
401  static google::spanner::v1::Type MakeTypeProto(char const*);
402  template <typename T>
403  static google::spanner::v1::Type MakeTypeProto(optional<T> const&) {
404  return MakeTypeProto(T{});
405  }
406  template <typename T>
407  static google::spanner::v1::Type MakeTypeProto(std::vector<T> const& v) {
408  google::spanner::v1::Type t;
409  t.set_code(google::spanner::v1::TypeCode::ARRAY);
410  *t.mutable_array_element_type() = MakeTypeProto(v.empty() ? T{} : v[0]);
411  // Checks that vector elements have exactly the same proto Type, which
412  // includes field names. This is documented UB.
413  for (auto const& e : v) {
414  if (!google::protobuf::util::MessageDifferencer::Equals(
415  MakeTypeProto(e), t.array_element_type())) {
416  google::cloud::internal::ThrowInvalidArgument("Mismatched types");
417  }
418  }
419  return t;
420  }
421  template <typename... Ts>
422  static google::spanner::v1::Type MakeTypeProto(std::tuple<Ts...> const& tup) {
423  google::spanner::v1::Type t;
424  t.set_code(google::spanner::v1::TypeCode::STRUCT);
425  internal::ForEach(tup, AddStructTypes{}, *t.mutable_struct_type());
426  return t;
427  }
428 
429  // A functor to be used with internal::ForEach to add type protos for all the
430  // elements of a tuple.
431  struct AddStructTypes {
432  template <typename T>
433  void operator()(T const& t,
434  google::spanner::v1::StructType& struct_type) const {
435  auto* field = struct_type.add_fields();
436  *field->mutable_type() = MakeTypeProto(t);
437  }
438  template <typename S, typename T,
439  typename std::enable_if<
440  std::is_convertible<S, std::string>::value, int>::type = 0>
441  void operator()(std::pair<S, T> const& p,
442  google::spanner::v1::StructType& struct_type) const {
443  auto* field = struct_type.add_fields();
444  field->set_name(p.first);
445  *field->mutable_type() = MakeTypeProto(p.second);
446  }
447  };
448 
449  // Encodes the argument as a protobuf according to the rules described in
450  // https://github.com/googleapis/googleapis/blob/master/google/spanner/v1/type.proto
451  static google::protobuf::Value MakeValueProto(bool b);
452  static google::protobuf::Value MakeValueProto(std::int64_t i);
453  static google::protobuf::Value MakeValueProto(double d);
454  static google::protobuf::Value MakeValueProto(std::string s);
455  static google::protobuf::Value MakeValueProto(Bytes b);
456  static google::protobuf::Value MakeValueProto(Timestamp ts);
457  static google::protobuf::Value MakeValueProto(CommitTimestamp ts);
458  static google::protobuf::Value MakeValueProto(Date d);
459  static google::protobuf::Value MakeValueProto(int i);
460  static google::protobuf::Value MakeValueProto(char const* s);
461  template <typename T>
462  static google::protobuf::Value MakeValueProto(optional<T> opt) {
463  if (opt.has_value()) return MakeValueProto(*std::move(opt));
464  google::protobuf::Value v;
465  v.set_null_value(google::protobuf::NullValue::NULL_VALUE);
466  return v;
467  }
468  template <typename T>
469  static google::protobuf::Value MakeValueProto(std::vector<T> vec) {
470  google::protobuf::Value v;
471  auto& list = *v.mutable_list_value();
472  for (auto&& e : vec) {
473  *list.add_values() = MakeValueProto(std::move(e));
474  }
475  return v;
476  }
477  template <typename... Ts>
478  static google::protobuf::Value MakeValueProto(std::tuple<Ts...> tup) {
479  google::protobuf::Value v;
480  internal::ForEach(tup, AddStructValues{}, *v.mutable_list_value());
481  return v;
482  }
483 
484  // A functor to be used with internal::ForEach to add Value protos for all
485  // the elements of a tuple.
486  struct AddStructValues {
487  template <typename T>
488  void operator()(T& t, google::protobuf::ListValue& list_value) const {
489  *list_value.add_values() = MakeValueProto(std::move(t));
490  }
491  template <typename S, typename T,
492  typename std::enable_if<
493  std::is_convertible<S, std::string>::value, int>::type = 0>
494  void operator()(std::pair<S, T> p,
495  google::protobuf::ListValue& list_value) const {
496  *list_value.add_values() = MakeValueProto(std::move(p.second));
497  }
498  };
499 
500  // Tag-dispatch overloads to extract a C++ value from a `Value` protobuf. The
501  // first argument type is the tag, its value is ignored.
502  static StatusOr<bool> GetValue(bool, google::protobuf::Value const&,
503  google::spanner::v1::Type const&);
504  static StatusOr<std::int64_t> GetValue(std::int64_t,
505  google::protobuf::Value const&,
506  google::spanner::v1::Type const&);
507  static StatusOr<double> GetValue(double, google::protobuf::Value const&,
508  google::spanner::v1::Type const&);
509  static StatusOr<std::string> GetValue(std::string const&,
510  google::protobuf::Value const&,
511  google::spanner::v1::Type const&);
512  static StatusOr<std::string> GetValue(std::string const&,
513  google::protobuf::Value&&,
514  google::spanner::v1::Type const&);
515  static StatusOr<Bytes> GetValue(Bytes const&, google::protobuf::Value const&,
516  google::spanner::v1::Type const&);
517  static StatusOr<Timestamp> GetValue(Timestamp, google::protobuf::Value const&,
518  google::spanner::v1::Type const&);
519  static StatusOr<CommitTimestamp> GetValue(CommitTimestamp,
520  google::protobuf::Value const&,
521  google::spanner::v1::Type const&);
522  static StatusOr<Date> GetValue(Date, google::protobuf::Value const&,
523  google::spanner::v1::Type const&);
524  template <typename T, typename V>
525  static StatusOr<optional<T>> GetValue(optional<T> const&, V&& pv,
526  google::spanner::v1::Type const& pt) {
527  if (pv.kind_case() == google::protobuf::Value::kNullValue) {
528  return optional<T>{};
529  }
530  auto value = GetValue(T{}, std::forward<V>(pv), pt);
531  if (!value) return std::move(value).status();
532  return optional<T>{*std::move(value)};
533  }
534  template <typename T, typename V>
535  static StatusOr<std::vector<T>> GetValue(
536  std::vector<T> const&, V&& pv, google::spanner::v1::Type const& pt) {
537  if (pv.kind_case() != google::protobuf::Value::kListValue) {
538  return Status(StatusCode::kUnknown, "missing ARRAY");
539  }
540  std::vector<T> v;
541  for (int i = 0; i < pv.list_value().values().size(); ++i) {
542  auto&& e = GetProtoListValueElement(std::forward<V>(pv), i);
543  using ET = decltype(e);
544  auto value = GetValue(T{}, std::forward<ET>(e), pt.array_element_type());
545  if (!value) return std::move(value).status();
546  v.push_back(*std::move(value));
547  }
548  return v;
549  }
550  template <typename V, typename... Ts>
551  static StatusOr<std::tuple<Ts...>> GetValue(
552  std::tuple<Ts...> const&, V&& pv, google::spanner::v1::Type const& pt) {
553  if (pv.kind_case() != google::protobuf::Value::kListValue) {
554  return Status(StatusCode::kUnknown, "missing STRUCT");
555  }
556  std::tuple<Ts...> tup;
557  Status status; // OK
558  ExtractTupleValues<V> f{status, 0, std::forward<V>(pv), pt};
559  internal::ForEach(tup, f);
560  if (!status.ok()) return status;
561  return tup;
562  }
563 
564  // A functor to be used with internal::ForEach to extract C++ types from a
565  // ListValue proto and store then in a tuple.
566  template <typename V>
567  struct ExtractTupleValues {
568  Status& status;
569  int i;
570  V&& pv;
571  google::spanner::v1::Type const& type;
572  template <typename T>
573  void operator()(T& t) {
574  auto&& e = GetProtoListValueElement(std::forward<V>(pv), i);
575  using ET = decltype(e);
576  auto value = GetValue(T{}, std::forward<ET>(e), type);
577  ++i;
578  if (!value) {
579  status = std::move(value).status();
580  } else {
581  t = *std::move(value);
582  }
583  }
584  template <typename T>
585  void operator()(std::pair<std::string, T>& p) {
586  p.first = type.struct_type().fields(i).name();
587  auto&& e = GetProtoListValueElement(std::forward<V>(pv), i);
588  using ET = decltype(e);
589  auto value = GetValue(T{}, std::forward<ET>(e), type);
590  ++i;
591  if (!value) {
592  status = std::move(value).status();
593  } else {
594  p.second = *std::move(value);
595  }
596  }
597  };
598 
599  // Protocol buffers are not friendly to generic programming, because they use
600  // different syntax and different names for mutable and non-mutable
601  // functions. To make GetValue(vector<T>, ...) (above) work, we need split
602  // the different protobuf syntaxes into overloaded functions.
603  static google::protobuf::Value const& GetProtoListValueElement(
604  google::protobuf::Value const& pv, int pos) {
605  return pv.list_value().values(pos);
606  }
607  static google::protobuf::Value&& GetProtoListValueElement(
608  google::protobuf::Value&& pv, int pos) {
609  return std::move(*pv.mutable_list_value()->mutable_values(pos));
610  }
611 
612  // A private templated constructor that is called by all the public
613  // constructors to set the type_ and value_ members. The `PrivateConstructor`
614  // type is used so that this overload is never chosen for
615  // non-member/non-friend callers. Otherwise, since visibility restrictions
616  // apply after overload resolution, users could get weird error messages if
617  // this constructor matched their arguments best.
618  struct PrivateConstructor {};
619  template <typename T>
620  Value(PrivateConstructor, T&& t)
621  : type_(MakeTypeProto(t)), value_(MakeValueProto(std::forward<T>(t))) {}
622 
623  Value(google::spanner::v1::Type t, google::protobuf::Value v)
624  : type_(std::move(t)), value_(std::move(v)) {}
625 
626  friend Value internal::FromProto(google::spanner::v1::Type,
627  google::protobuf::Value);
628  friend std::pair<google::spanner::v1::Type, google::protobuf::Value>
629  internal::ToProto(Value);
630 
631  google::spanner::v1::Type type_;
632  google::protobuf::Value value_;
633 };
634 
642 template <typename T>
644  return Value(optional<T>{});
645 }
646 
647 } // namespace SPANNER_CLIENT_NS
648 } // namespace spanner
649 } // namespace cloud
650 } // namespace google
651 
652 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_VALUE_H
Value(std::tuple< Ts... > tup)
Constructs an instance from a Spanner STRUCT with a type and values matching the given std::tuple.
Definition: value.h:250
A sentinel type used to update a commit timestamp column.
Definition: timestamp.h:204
The Value class represents a type-safe, nullable Spanner value.
Definition: value.h:162
Value(int v)
Constructs an instance from common C++ literal types that closely, though not exactly,...
Definition: value.h:212
Value(Bytes v)
Constructs an instance with the specified type and value.
Definition: value.h:186
Value MakeNullValue()
Factory to construct a "null" Value of the specified type T.
Definition: value.h:643
bool operator==(Backup const &a, Backup const &b)
Definition: backup.cc:29
friend void PrintTo(Value const &v, std::ostream *os)
Prints the same output as operator<<.
Definition: value.h:329
Value(std::string v)
Constructs an instance with the specified type and value.
Definition: value.h:184
Value(char const *v)
Constructs an instance from common C++ literal types that closely, though not exactly,...
Definition: value.h:214
Contains all the Cloud Spanner C++ client types and functions.
Value(bool v)
Constructs an instance with the specified type and value.
Definition: value.h:178
Value(double v)
Constructs an instance with the specified type and value.
Definition: value.h:182
A representation of the Spanner BYTES type: variable-length binary data.
Definition: bytes.h:45
Represents a date in the proleptic Gregorian calendar as a triple of year, month (1-12),...
Definition: date.h:35
Value(std::int64_t v)
Constructs an instance with the specified type and value.
Definition: value.h:180
Value(std::vector< T > v)
Constructs an instance from a Spanner ARRAY of the specified type and values.
Definition: value.h:237
StatusOr< T > get() const &
Returns the contained value wrapped in a google::cloud::StatusOr<T>.
Definition: value.h:286
#define SPANNER_CLIENT_NS
Definition: version.h:22
Value(optional< T > opt)
Constructs a non-null instance if opt has a value, otherwise constructs a null instance with the spec...
Definition: value.h:221
Value(Timestamp v)
Constructs an instance with the specified type and value.
Definition: value.h:188
StatusOr< T > get() &&
Returns the contained value wrapped in a google::cloud::StatusOr<T>.
Definition: value.h:298
Value(CommitTimestamp v)
Constructs an instance with the specified type and value.
Definition: value.h:190
friend bool operator!=(Value const &a, Value const &b)
Definition: value.h:254
Value(Date v)
Constructs an instance with the specified type and value.
Definition: value.h:193
A representation of the Spanner TIMESTAMP type: An instant in time.
Definition: timestamp.h:73
std::ostream & operator<<(std::ostream &os, Backup const &bn)
Definition: backup.cc:35