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