Google Cloud Spanner C++ Client
A C++ Client Library for Google Cloud Spanner
value.cc
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 
16 #include "google/cloud/spanner/internal/date.h"
17 #include "google/cloud/log.h"
18 #include <cerrno>
19 #include <cmath>
20 #include <cstdlib>
21 #include <cstring>
22 #include <ios>
23 #include <string>
24 
25 namespace google {
26 namespace cloud {
27 namespace spanner {
28 inline namespace SPANNER_CLIENT_NS {
29 
30 namespace {
31 
32 // Compares two sets of Type and Value protos for equality. This method calls
33 // itself recursively to compare subtypes and subvalues.
34 bool Equal(google::spanner::v1::Type const& pt1,
35  google::protobuf::Value const& pv1,
36  google::spanner::v1::Type const& pt2,
37  google::protobuf::Value const& pv2) {
38  if (pt1.code() != pt2.code()) return false;
39  if (pv1.kind_case() != pv2.kind_case()) return false;
40  switch (pt1.code()) {
41  case google::spanner::v1::TypeCode::BOOL:
42  return pv1.bool_value() == pv2.bool_value();
43  case google::spanner::v1::TypeCode::INT64:
44  return pv1.string_value() == pv2.string_value();
45  case google::spanner::v1::TypeCode::FLOAT64:
46  // NaN should always compare not equal, even to itself.
47  if (pv1.string_value() == "NaN" || pv2.string_value() == "NaN") {
48  return false;
49  }
50  return pv1.string_value() == pv2.string_value() &&
51  pv1.number_value() == pv2.number_value();
52  case google::spanner::v1::TypeCode::STRING:
53  case google::spanner::v1::TypeCode::BYTES:
54  case google::spanner::v1::TypeCode::DATE:
55  case google::spanner::v1::TypeCode::TIMESTAMP:
56  return pv1.string_value() == pv2.string_value();
57  case google::spanner::v1::TypeCode::ARRAY: {
58  auto const& etype1 = pt1.array_element_type();
59  auto const& etype2 = pt2.array_element_type();
60  if (etype1.code() != etype2.code()) return false;
61  auto const& v1 = pv1.list_value().values();
62  auto const& v2 = pv2.list_value().values();
63  if (v1.size() != v2.size()) return false;
64  for (int i = 0; i < v1.size(); ++i) {
65  if (!Equal(etype1, v1.Get(i), etype1, v2.Get(i))) {
66  return false;
67  }
68  }
69  return true;
70  }
71  case google::spanner::v1::TypeCode::STRUCT: {
72  auto const& fields1 = pt1.struct_type().fields();
73  auto const& fields2 = pt2.struct_type().fields();
74  if (fields1.size() != fields2.size()) return false;
75  auto const& v1 = pv1.list_value().values();
76  auto const& v2 = pv2.list_value().values();
77  if (fields1.size() != v1.size() || v1.size() != v2.size()) return false;
78  for (int i = 0; i < fields1.size(); ++i) {
79  auto const& f1 = fields1.Get(i);
80  auto const& f2 = fields2.Get(i);
81  if (f1.name() != f2.name()) return false;
82  if (!Equal(f1.type(), v1.Get(i), f2.type(), v2.Get(i))) {
83  return false;
84  }
85  }
86  return true;
87  }
88  default:
89  return true;
90  }
91 }
92 
93 // A helper to escape all double quotes in the given string `s`. For example,
94 // if given `"foo"`, outputs `\"foo\"`. This is useful when a caller needs to
95 // wrap `s` itself in double quotes.
96 std::ostream& EscapeQuotes(std::ostream& os, std::string const& s) {
97  for (auto const& c : s) {
98  if (c == '"') os << "\\";
99  os << c;
100  }
101  return os;
102 }
103 
104 // An enum to tell StreamHelper() whether a value is being printed as a scalar
105 // or as part of an aggregate type (i.e., a vector or tuple). Some types may
106 // format themselves differently in each case.
107 enum class StreamMode { kScalar, kAggregate };
108 
109 std::ostream& StreamHelper(std::ostream& os, google::protobuf::Value const& v,
110  google::spanner::v1::Type const& t,
111  StreamMode mode) {
112  if (v.kind_case() == google::protobuf::Value::kNullValue) {
113  return os << "NULL";
114  }
115 
116  switch (t.code()) {
117  case google::spanner::v1::TypeCode::BOOL:
118  return os << v.bool_value();
119 
120  case google::spanner::v1::TypeCode::INT64:
121  return os << internal::FromProto(t, v).get<std::int64_t>().value();
122 
123  case google::spanner::v1::TypeCode::FLOAT64:
124  return os << internal::FromProto(t, v).get<double>().value();
125 
126  case google::spanner::v1::TypeCode::STRING:
127  switch (mode) {
128  case StreamMode::kScalar:
129  return os << v.string_value();
130  case StreamMode::kAggregate:
131  os << '"';
132  EscapeQuotes(os, v.string_value());
133  return os << '"';
134  }
135  return os; // Unreachable, but quiets warning.
136 
137  case google::spanner::v1::TypeCode::BYTES:
138  return os << internal::BytesFromBase64(v.string_value()).value();
139 
140  case google::spanner::v1::TypeCode::TIMESTAMP:
141  case google::spanner::v1::TypeCode::DATE:
142  return os << v.string_value();
143 
144  case google::spanner::v1::TypeCode::ARRAY: {
145  const char* delimiter = "";
146  os << '[';
147  for (auto const& e : v.list_value().values()) {
148  os << delimiter;
149  StreamHelper(os, e, t.array_element_type(), StreamMode::kAggregate);
150  delimiter = ", ";
151  }
152  return os << ']';
153  }
154 
155  case google::spanner::v1::TypeCode::STRUCT: {
156  const char* delimiter = "";
157  os << '(';
158  for (int i = 0; i < v.list_value().values_size(); ++i) {
159  os << delimiter;
160  if (!t.struct_type().fields(i).name().empty()) {
161  os << '"';
162  EscapeQuotes(os, t.struct_type().fields(i).name());
163  os << '"' << ": ";
164  }
165  StreamHelper(os, v.list_value().values(i),
166  t.struct_type().fields(i).type(), StreamMode::kAggregate);
167  delimiter = ", ";
168  }
169  return os << ')';
170  }
171 
172  default:
173  return os << "Error: unknown value type code " << t.code();
174  }
175  return os;
176 }
177 
178 } // namespace
179 
180 namespace internal {
181 
182 Value FromProto(google::spanner::v1::Type t, google::protobuf::Value v) {
183  return Value(std::move(t), std::move(v));
184 }
185 
186 std::pair<google::spanner::v1::Type, google::protobuf::Value> ToProto(Value v) {
187  return std::make_pair(std::move(v.type_), std::move(v.value_));
188 }
189 
190 } // namespace internal
191 
192 bool operator==(Value const& a, Value const& b) {
193  return Equal(a.type_, a.value_, b.type_, b.value_);
194 }
195 
196 std::ostream& operator<<(std::ostream& os, Value const& v) {
197  return StreamHelper(os, v.value_, v.type_, StreamMode::kScalar);
198 }
199 
200 //
201 // Value::TypeProtoIs
202 //
203 
204 bool Value::TypeProtoIs(bool, google::spanner::v1::Type const& type) {
205  return type.code() == google::spanner::v1::TypeCode::BOOL;
206 }
207 
208 bool Value::TypeProtoIs(std::int64_t, google::spanner::v1::Type const& type) {
209  return type.code() == google::spanner::v1::TypeCode::INT64;
210 }
211 
212 bool Value::TypeProtoIs(double, google::spanner::v1::Type const& type) {
213  return type.code() == google::spanner::v1::TypeCode::FLOAT64;
214 }
215 
216 bool Value::TypeProtoIs(Timestamp, google::spanner::v1::Type const& type) {
217  return type.code() == google::spanner::v1::TypeCode::TIMESTAMP;
218 }
219 
220 bool Value::TypeProtoIs(CommitTimestamp,
221  google::spanner::v1::Type const& type) {
222  return type.code() == google::spanner::v1::TypeCode::TIMESTAMP;
223 }
224 
225 bool Value::TypeProtoIs(Date, google::spanner::v1::Type const& type) {
226  return type.code() == google::spanner::v1::TypeCode::DATE;
227 }
228 
229 bool Value::TypeProtoIs(std::string const&,
230  google::spanner::v1::Type const& type) {
231  return type.code() == google::spanner::v1::TypeCode::STRING;
232 }
233 
234 bool Value::TypeProtoIs(Bytes const&, google::spanner::v1::Type const& type) {
235  return type.code() == google::spanner::v1::TypeCode::BYTES;
236 }
237 
238 //
239 // Value::MakeTypeProto
240 //
241 
242 google::spanner::v1::Type Value::MakeTypeProto(bool) {
243  google::spanner::v1::Type t;
244  t.set_code(google::spanner::v1::TypeCode::BOOL);
245  return t;
246 }
247 
248 google::spanner::v1::Type Value::MakeTypeProto(std::int64_t) {
249  google::spanner::v1::Type t;
250  t.set_code(google::spanner::v1::TypeCode::INT64);
251  return t;
252 }
253 
254 google::spanner::v1::Type Value::MakeTypeProto(double) {
255  google::spanner::v1::Type t;
256  t.set_code(google::spanner::v1::TypeCode::FLOAT64);
257  return t;
258 }
259 
260 google::spanner::v1::Type Value::MakeTypeProto(std::string const&) {
261  google::spanner::v1::Type t;
262  t.set_code(google::spanner::v1::TypeCode::STRING);
263  return t;
264 }
265 
266 google::spanner::v1::Type Value::MakeTypeProto(Bytes const&) {
267  google::spanner::v1::Type t;
268  t.set_code(google::spanner::v1::TypeCode::BYTES);
269  return t;
270 }
271 
272 google::spanner::v1::Type Value::MakeTypeProto(Timestamp) {
273  google::spanner::v1::Type t;
274  t.set_code(google::spanner::v1::TypeCode::TIMESTAMP);
275  return t;
276 }
277 
278 google::spanner::v1::Type Value::MakeTypeProto(CommitTimestamp) {
279  google::spanner::v1::Type t;
280  t.set_code(google::spanner::v1::TypeCode::TIMESTAMP);
281  return t;
282 }
283 
284 google::spanner::v1::Type Value::MakeTypeProto(Date) {
285  google::spanner::v1::Type t;
286  t.set_code(google::spanner::v1::TypeCode::DATE);
287  return t;
288 }
289 
290 google::spanner::v1::Type Value::MakeTypeProto(int) {
291  return MakeTypeProto(std::int64_t{});
292 }
293 
294 google::spanner::v1::Type Value::MakeTypeProto(char const*) {
295  return MakeTypeProto(std::string{});
296 }
297 
298 //
299 // Value::MakeValueProto
300 //
301 
302 google::protobuf::Value Value::MakeValueProto(bool b) {
303  google::protobuf::Value v;
304  v.set_bool_value(b);
305  return v;
306 }
307 
308 google::protobuf::Value Value::MakeValueProto(std::int64_t i) {
309  google::protobuf::Value v;
310  v.set_string_value(std::to_string(i));
311  return v;
312 }
313 
314 google::protobuf::Value Value::MakeValueProto(double d) {
315  google::protobuf::Value v;
316  if (std::isnan(d)) {
317  v.set_string_value("NaN");
318  } else if (std::isinf(d)) {
319  v.set_string_value(d < 0 ? "-Infinity" : "Infinity");
320  } else {
321  v.set_number_value(d);
322  }
323  return v;
324 }
325 
326 google::protobuf::Value Value::MakeValueProto(std::string s) {
327  google::protobuf::Value v;
328  v.set_string_value(std::move(s));
329  return v;
330 }
331 
332 google::protobuf::Value Value::MakeValueProto(Bytes bytes) {
333  google::protobuf::Value v;
334  v.set_string_value(internal::BytesToBase64(std::move(bytes)));
335  return v;
336 }
337 
338 google::protobuf::Value Value::MakeValueProto(Timestamp ts) {
339  google::protobuf::Value v;
340  v.set_string_value(internal::TimestampToRFC3339(ts));
341  return v;
342 }
343 
344 google::protobuf::Value Value::MakeValueProto(CommitTimestamp) {
345  google::protobuf::Value v;
346  v.set_string_value("spanner.commit_timestamp()");
347  return v;
348 }
349 
350 google::protobuf::Value Value::MakeValueProto(Date d) {
351  google::protobuf::Value v;
352  v.set_string_value(internal::DateToString(d));
353  return v;
354 }
355 
356 google::protobuf::Value Value::MakeValueProto(int i) {
357  return MakeValueProto(std::int64_t{i});
358 }
359 
360 google::protobuf::Value Value::MakeValueProto(char const* s) {
361  return MakeValueProto(std::string(s));
362 }
363 
364 //
365 // Value::GetValue
366 //
367 
368 StatusOr<bool> Value::GetValue(bool, google::protobuf::Value const& pv,
369  google::spanner::v1::Type const&) {
370  if (pv.kind_case() != google::protobuf::Value::kBoolValue) {
371  return Status(StatusCode::kUnknown, "missing BOOL");
372  }
373  return pv.bool_value();
374 }
375 
376 StatusOr<std::int64_t> Value::GetValue(std::int64_t,
377  google::protobuf::Value const& pv,
378  google::spanner::v1::Type const&) {
379  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
380  return Status(StatusCode::kUnknown, "missing INT64");
381  }
382  auto const& s = pv.string_value();
383  char* end = nullptr;
384  errno = 0;
385  std::int64_t x = {std::strtoll(s.c_str(), &end, 10)};
386  if (errno != 0) {
387  auto const err = std::string(std::strerror(errno));
388  return Status(StatusCode::kUnknown, err + ": \"" + s + "\"");
389  }
390  if (end == s.c_str()) {
391  return Status(StatusCode::kUnknown, "No numeric conversion: \"" + s + "\"");
392  }
393  if (*end != '\0') {
394  return Status(StatusCode::kUnknown, "Trailing data: \"" + s + "\"");
395  }
396  return x;
397 }
398 
399 StatusOr<double> Value::GetValue(double, google::protobuf::Value const& pv,
400  google::spanner::v1::Type const&) {
401  if (pv.kind_case() == google::protobuf::Value::kNumberValue) {
402  return pv.number_value();
403  }
404  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
405  return Status(StatusCode::kUnknown, "missing FLOAT64");
406  }
407  std::string const& s = pv.string_value();
408  auto const inf = std::numeric_limits<double>::infinity();
409  if (s == "-Infinity") return -inf;
410  if (s == "Infinity") return inf;
411  if (s == "NaN") return std::nan("");
412  return Status(StatusCode::kUnknown, "bad FLOAT64 data: \"" + s + "\"");
413 }
414 
415 StatusOr<std::string> Value::GetValue(std::string const&,
416  google::protobuf::Value const& pv,
417  google::spanner::v1::Type const&) {
418  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
419  return Status(StatusCode::kUnknown, "missing STRING");
420  }
421  return pv.string_value();
422 }
423 
424 StatusOr<std::string> Value::GetValue(std::string const&,
425  google::protobuf::Value&& pv,
426  google::spanner::v1::Type const&) {
427  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
428  return Status(StatusCode::kUnknown, "missing STRING");
429  }
430  return std::move(*pv.mutable_string_value());
431 }
432 
433 StatusOr<Bytes> Value::GetValue(Bytes const&, google::protobuf::Value const& pv,
434  google::spanner::v1::Type const&) {
435  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
436  return Status(StatusCode::kUnknown, "missing BYTES");
437  }
438  auto decoded = internal::BytesFromBase64(pv.string_value());
439  if (!decoded) return decoded.status();
440  return *decoded;
441 }
442 
443 StatusOr<Timestamp> Value::GetValue(Timestamp,
444  google::protobuf::Value const& pv,
445  google::spanner::v1::Type const&) {
446  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
447  return Status(StatusCode::kUnknown, "missing TIMESTAMP");
448  }
449  return internal::TimestampFromRFC3339(pv.string_value());
450 }
451 
452 StatusOr<CommitTimestamp> Value::GetValue(CommitTimestamp,
453  google::protobuf::Value const& pv,
454  google::spanner::v1::Type const&) {
455  if (pv.kind_case() != google::protobuf::Value::kStringValue ||
456  pv.string_value() != "spanner.commit_timestamp()") {
457  return Status(StatusCode::kUnknown, "invalid commit_timestamp");
458  }
459  return CommitTimestamp{};
460 }
461 
462 StatusOr<Date> Value::GetValue(Date, google::protobuf::Value const& pv,
463  google::spanner::v1::Type const&) {
464  if (pv.kind_case() != google::protobuf::Value::kStringValue) {
465  return Status(StatusCode::kUnknown, "missing DATE");
466  }
467  return internal::DateFromString(pv.string_value());
468 }
469 
470 } // namespace SPANNER_CLIENT_NS
471 } // namespace spanner
472 } // namespace cloud
473 } // namespace google
The Value class represents a type-safe, nullable Spanner value.
Definition: value.h:162
std::ostream & operator<<(std::ostream &os, Value const &v)
Definition: value.cc:196
bool operator==(Value const &a, Value const &b)
Definition: value.cc:192
Contains all the Cloud Spanner C++ client types and functions.
#define SPANNER_CLIENT_NS
Definition: version.h:22