1717#include " google/cloud/internal/throw_delegate.h"
1818#include " absl/container/flat_hash_set.h"
1919#include " absl/strings/cord.h"
20+ #include " absl/strings/substitute.h"
2021#include < google/bigtable/v2/types.pb.h>
2122#include < google/protobuf/descriptor.h>
2223#include < google/protobuf/message.h>
@@ -207,12 +208,6 @@ bool MapEqual( // NOLINT(misc-no-recursion)
207208 comparison_function);
208209}
209210
210- // From the proto description, `NULL` values are represented by having a kind
211- // equal to KIND_NOT_SET
212- bool IsNullValue (google::bigtable::v2::Value const & value) {
213- return value.kind_case () == google::bigtable::v2::Value::KIND_NOT_SET;
214- }
215-
216211// A helper to escape all double quotes in the given string `s`. For example,
217212// if given `"foo"`, outputs `\"foo\"`. This is useful when a caller needs to
218213// wrap `s` itself in double quotes.
@@ -238,7 +233,7 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
238233 google::bigtable::v2::Value const & v,
239234 google::bigtable::v2::Type const & t,
240235 StreamMode mode) {
241- if (IsNullValue (v)) {
236+ if (Value:: IsNullValue (v)) {
242237 return os << " NULL" ;
243238 }
244239
@@ -344,6 +339,161 @@ std::ostream& operator<<(std::ostream& os, Value const& v) {
344339 return StreamHelper (os, v.value_ , v.type_ , StreamMode::kScalar );
345340}
346341
342+ // NOLINTNEXTLINE(misc-no-recursion)
343+ Status Value::TypeAndArrayValuesMatch (
344+ google::bigtable::v2::Type const & type,
345+ google::bigtable::v2::Value const & value) {
346+ if (!value.has_array_value ()) {
347+ return internal::InternalError (
348+ " Value kind must be ARRAY_VALUE for columns of type: MAP" );
349+ }
350+ auto const & vals = value.array_value ().values ();
351+ for (auto const & val : vals) {
352+ auto const element_match_result =
353+ TypeAndValuesMatch (type.array_type ().element_type (), val);
354+ if (!element_match_result.ok ()) {
355+ return element_match_result;
356+ }
357+ }
358+ return Status{};
359+ }
360+
361+ // NOLINTNEXTLINE(misc-no-recursion)
362+ Status Value::TypeAndMapValuesMatch (google::bigtable::v2::Type const & type,
363+ google::bigtable::v2::Value const & value) {
364+ if (!value.has_array_value ()) {
365+ return internal::InternalError (
366+ " Value kind must be ARRAY_VALUE for columns of type: MAP" );
367+ }
368+ auto key_type = type.map_type ().key_type ();
369+ auto value_type = type.map_type ().value_type ();
370+ auto const & vals = value.array_value ().values ();
371+ for (auto const & val : vals) {
372+ if (!val.has_array_value () || val.array_value ().values_size () != 2 ) {
373+ return internal::InternalError (
374+ " ARRAY_VALUE must contain entries of 2 values" );
375+ }
376+ auto map_key = val.array_value ().values (0 );
377+ auto map_value = val.array_value ().values (1 );
378+ // NOLINTNEXTLINE(misc-no-recursion)
379+ auto key_match_result = TypeAndValuesMatch (key_type, map_key);
380+ if (!key_match_result.ok ()) {
381+ return key_match_result;
382+ }
383+ // NOLINTNEXTLINE(misc-no-recursion)
384+ auto value_match_result = TypeAndValuesMatch (value_type, map_value);
385+ if (!value_match_result.ok ()) {
386+ return value_match_result;
387+ }
388+ }
389+ return Status{};
390+ }
391+
392+ // NOLINTNEXTLINE(misc-no-recursion)
393+ Status Value::TypeAndStructValuesMatch (
394+ google::bigtable::v2::Type const & type,
395+ google::bigtable::v2::Value const & value) {
396+ if (!value.has_array_value ()) {
397+ return internal::InternalError (
398+ " Value kind must be ARRAY_VALUE for columns of type: STRUCT" );
399+ }
400+ auto fields = type.struct_type ().fields ();
401+ auto values = value.array_value ().values ();
402+ if (fields.size () != values.size ()) {
403+ auto const message = absl::Substitute (
404+ " received Struct with $0 values, but metadata has $1 fields" ,
405+ values.size (), fields.size ());
406+ return internal::InternalError (message);
407+ }
408+ for (int i = 0 ; i < fields.size (); ++i) {
409+ auto const & f1 = fields.Get (i);
410+ auto const & v = values[i];
411+ auto match_result = TypeAndValuesMatch (f1.type (), v);
412+ if (!match_result.ok ()) {
413+ return match_result;
414+ }
415+ }
416+ return Status{};
417+ }
418+
419+ /* *
420+ * Checks whether the declared type in column matches the value's contents.
421+ * Since the received values may or may not have type() set, we check against
422+ * the value contents themselves
423+ */
424+ // NOLINTNEXTLINE(misc-no-recursion)
425+ Status Value::TypeAndValuesMatch (google::bigtable::v2::Type const & type,
426+ google::bigtable::v2::Value const & value) {
427+ using google::bigtable::v2::Type;
428+ auto make_mismatch_metadata_status = [&](std::string const & value_kind,
429+ std::string const & type_name) {
430+ auto const message = absl::Substitute (
431+ " Value kind must be $0 for columns of type: $1" , value_kind, type_name);
432+ return internal::InternalError (message);
433+ };
434+ // Null values are allowed by default
435+ if (IsNullValue (value)) {
436+ return Status{};
437+ }
438+ Status result;
439+ switch (type.kind_case ()) {
440+ case Type::kArrayType :
441+ result = TypeAndArrayValuesMatch (type, value);
442+ break ;
443+ case Type::kMapType :
444+ result = TypeAndMapValuesMatch (type, value);
445+ break ;
446+ case Type::kStructType :
447+ result = TypeAndStructValuesMatch (type, value);
448+ break ;
449+ case Type::kBoolType :
450+ if (!value.has_bool_value ()) {
451+ result = make_mismatch_metadata_status (" BOOL_VALUE" , " BOOL" );
452+ }
453+ break ;
454+ case Type::kBytesType :
455+ if (!value.has_bytes_value ()) {
456+ result = make_mismatch_metadata_status (" BYTES_VALUE" , " BYTES" );
457+ }
458+ break ;
459+ case Type::kDateType :
460+ if (!value.has_date_value ()) {
461+ result = make_mismatch_metadata_status (" DATE_VALUE" , " DATE" );
462+ }
463+ break ;
464+ case Type::kFloat32Type :
465+ if (!value.has_float_value ()) {
466+ result = make_mismatch_metadata_status (" FLOAT_VALUE" , " FLOAT32" );
467+ }
468+ break ;
469+ case Type::kFloat64Type :
470+ if (!value.has_float_value ()) {
471+ result = make_mismatch_metadata_status (" FLOAT_VALUE" , " FLOAT64" );
472+ }
473+ break ;
474+ case Type::kInt64Type :
475+ if (!value.has_int_value ()) {
476+ result = make_mismatch_metadata_status (" INT_VALUE" , " INT64" );
477+ }
478+ break ;
479+ case Type::kStringType :
480+ if (!value.has_string_value ()) {
481+ result = make_mismatch_metadata_status (" STRING_VALUE" , " STRING" );
482+ }
483+ break ;
484+ case Type::kTimestampType :
485+ if (!value.has_timestamp_value ()) {
486+ result = make_mismatch_metadata_status (" TIMESTAMP_VALUE" , " TIMESTAMP" );
487+ }
488+ break ;
489+ default :
490+ result = internal::InternalError (" Unsupported type" );
491+ break ;
492+ }
493+ // Nulls are allowed;
494+ return result;
495+ }
496+
347497//
348498// Value::TypeProtoIs
349499//
0 commit comments