@@ -30,6 +30,8 @@ namespace bigtable {
3030GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3131namespace {
3232
33+ auto constexpr kMaxValueDepth = 10 ;
34+
3335// Some Bigtable proto fields use Cord internally and string externally.
3436template <typename T, typename std::enable_if<
3537 std::is_same<T, std::string>::value>::type* = nullptr >
@@ -339,18 +341,32 @@ std::ostream& operator<<(std::ostream& os, Value const& v) {
339341 return StreamHelper (os, v.value_ , v.type_ , StreamMode::kScalar );
340342}
341343
344+ Status CheckDepthExceeded (int const depth) {
345+ if (depth > kMaxValueDepth ) {
346+ return internal::InternalError (
347+ absl::Substitute (" Nested value depth exceeds $0 levels" ,
348+ kMaxValueDepth ),
349+ GCP_ERROR_INFO ());
350+ }
351+ return Status{};
352+ }
353+
342354// NOLINTNEXTLINE(misc-no-recursion)
343- Status Value::TypeAndArrayValuesMatch (
344- google::bigtable::v2::Type const & type,
345- google::bigtable::v2::Value const & value) {
355+ Status TypeAndArrayValuesMatch (google::bigtable::v2::Type const & type,
356+ google::bigtable::v2::Value const & value,
357+ int const depth) {
358+ auto depth_exceeded = CheckDepthExceeded (depth);
359+ if (!depth_exceeded.ok ()) {
360+ return depth_exceeded;
361+ }
346362 if (!value.has_array_value ()) {
347363 return internal::InternalError (
348364 " Value kind must be ARRAY_VALUE for columns of type: MAP" );
349365 }
350366 auto const & vals = value.array_value ().values ();
351367 for (auto const & val : vals) {
352368 auto const element_match_result =
353- TypeAndValuesMatch (type.array_type ().element_type (), val);
369+ Value:: TypeAndValuesMatch (type.array_type ().element_type (), val, depth );
354370 if (!element_match_result.ok ()) {
355371 return element_match_result;
356372 }
@@ -359,8 +375,13 @@ Status Value::TypeAndArrayValuesMatch(
359375}
360376
361377// NOLINTNEXTLINE(misc-no-recursion)
362- Status Value::TypeAndMapValuesMatch (google::bigtable::v2::Type const & type,
363- google::bigtable::v2::Value const & value) {
378+ Status TypeAndMapValuesMatch (google::bigtable::v2::Type const & type,
379+ google::bigtable::v2::Value const & value,
380+ int const depth) {
381+ auto depth_exceeded = CheckDepthExceeded (depth);
382+ if (!depth_exceeded.ok ()) {
383+ return depth_exceeded;
384+ }
364385 if (!value.has_array_value ()) {
365386 return internal::InternalError (
366387 " Value kind must be ARRAY_VALUE for columns of type: MAP" );
@@ -376,12 +397,13 @@ Status Value::TypeAndMapValuesMatch(google::bigtable::v2::Type const& type,
376397 auto map_key = val.array_value ().values (0 );
377398 auto map_value = val.array_value ().values (1 );
378399 // NOLINTNEXTLINE(misc-no-recursion)
379- auto key_match_result = TypeAndValuesMatch (key_type, map_key);
400+ auto key_match_result = Value:: TypeAndValuesMatch (key_type, map_key, depth );
380401 if (!key_match_result.ok ()) {
381402 return key_match_result;
382403 }
383404 // NOLINTNEXTLINE(misc-no-recursion)
384- auto value_match_result = TypeAndValuesMatch (value_type, map_value);
405+ auto value_match_result =
406+ Value::TypeAndValuesMatch (value_type, map_value, depth);
385407 if (!value_match_result.ok ()) {
386408 return value_match_result;
387409 }
@@ -390,9 +412,13 @@ Status Value::TypeAndMapValuesMatch(google::bigtable::v2::Type const& type,
390412}
391413
392414// NOLINTNEXTLINE(misc-no-recursion)
393- Status Value::TypeAndStructValuesMatch (
394- google::bigtable::v2::Type const & type,
395- google::bigtable::v2::Value const & value) {
415+ Status TypeAndStructValuesMatch (google::bigtable::v2::Type const & type,
416+ google::bigtable::v2::Value const & value,
417+ int const depth) {
418+ auto depth_exceeded = CheckDepthExceeded (depth);
419+ if (!depth_exceeded.ok ()) {
420+ return depth_exceeded;
421+ }
396422 if (!value.has_array_value ()) {
397423 return internal::InternalError (
398424 " Value kind must be ARRAY_VALUE for columns of type: STRUCT" );
@@ -408,7 +434,7 @@ Status Value::TypeAndStructValuesMatch(
408434 for (int i = 0 ; i < fields.size (); ++i) {
409435 auto const & f1 = fields.Get (i);
410436 auto const & v = values[i];
411- auto match_result = TypeAndValuesMatch (f1.type (), v);
437+ auto match_result = Value:: TypeAndValuesMatch (f1.type (), v, depth );
412438 if (!match_result.ok ()) {
413439 return match_result;
414440 }
@@ -423,7 +449,12 @@ Status Value::TypeAndStructValuesMatch(
423449 */
424450// NOLINTNEXTLINE(misc-no-recursion)
425451Status Value::TypeAndValuesMatch (google::bigtable::v2::Type const & type,
426- google::bigtable::v2::Value const & value) {
452+ google::bigtable::v2::Value const & value,
453+ int const depth) {
454+ auto depth_exceeded = CheckDepthExceeded (depth);
455+ if (!depth_exceeded.ok ()) {
456+ return depth_exceeded;
457+ }
427458 using google::bigtable::v2::Type;
428459 auto make_mismatch_metadata_status = [&](std::string const & value_kind,
429460 std::string const & type_name) {
@@ -438,13 +469,13 @@ Status Value::TypeAndValuesMatch(google::bigtable::v2::Type const& type,
438469 Status result;
439470 switch (type.kind_case ()) {
440471 case Type::kArrayType :
441- result = TypeAndArrayValuesMatch (type, value);
472+ result = TypeAndArrayValuesMatch (type, value, depth + 1 );
442473 break ;
443474 case Type::kMapType :
444- result = TypeAndMapValuesMatch (type, value);
475+ result = TypeAndMapValuesMatch (type, value, depth + 1 );
445476 break ;
446477 case Type::kStructType :
447- result = TypeAndStructValuesMatch (type, value);
478+ result = TypeAndStructValuesMatch (type, value, depth + 1 );
448479 break ;
449480 case Type::kBoolType :
450481 if (!value.has_bool_value ()) {
0 commit comments