@@ -50,6 +50,24 @@ std::string AsString(T&& s) {
5050 std::move (s)); // NOLINT(bugprone-move-forwarding-reference)
5151}
5252
53+ // Forward declarations for mutually recursive functions.
54+ bool Equal (google::bigtable::v2::Type const & pt1, // NOLINT(misc-no-recursion)
55+ google::bigtable::v2::Value const & pv1,
56+ google::bigtable::v2::Type const & pt2,
57+ google::bigtable::v2::Value const & pv2);
58+
59+ bool ArrayEqual ( // NOLINT(misc-no-recursion)
60+ google::bigtable::v2::Type const & pt1,
61+ google::bigtable::v2::Value const & pv1,
62+ google::bigtable::v2::Type const & pt2,
63+ google::bigtable::v2::Value const & pv2);
64+
65+ bool StructEqual ( // NOLINT(misc-no-recursion)
66+ google::bigtable::v2::Type const & pt1,
67+ google::bigtable::v2::Value const & pv1,
68+ google::bigtable::v2::Type const & pt2,
69+ google::bigtable::v2::Value const & pv2);
70+
5371// Compares two sets of Type and Value protos for equality. This method calls
5472// itself recursively to compare subtypes and subvalues.
5573bool Equal (google::bigtable::v2::Type const & pt1, // NOLINT(misc-no-recursion)
@@ -83,24 +101,61 @@ bool Equal(google::bigtable::v2::Type const& pt1, // NOLINT(misc-no-recursion)
83101 pv1.date_value ().year () == pv2.date_value ().year ();
84102 }
85103 if (pt1.has_array_type ()) {
86- auto const & vec1 = pv1.array_value ().values ();
87- auto const & vec2 = pv2.array_value ().values ();
88- if (vec1.size () != vec2.size ()) {
104+ return ArrayEqual (pt1, pv1, pt2, pv2);
105+ }
106+ if (pt1.has_struct_type ()) {
107+ return StructEqual (pt1, pv1, pt2, pv2);
108+ }
109+ return false ;
110+ }
111+
112+ // Compares two sets of Type and Value protos that represent an ARRAY for
113+ // equality.
114+ bool ArrayEqual ( // NOLINT(misc-no-recursion)
115+ google::bigtable::v2::Type const & pt1,
116+ google::bigtable::v2::Value const & pv1,
117+ google::bigtable::v2::Type const & pt2,
118+ google::bigtable::v2::Value const & pv2) {
119+ auto const & vec1 = pv1.array_value ().values ();
120+ auto const & vec2 = pv2.array_value ().values ();
121+ if (vec1.size () != vec2.size ()) {
122+ return false ;
123+ }
124+ auto const & el_type1 = pt1.array_type ().element_type ();
125+ auto const & el_type2 = pt2.array_type ().element_type ();
126+ if (el_type1.kind_case () != el_type2.kind_case ()) {
127+ return false ;
128+ }
129+ for (int i = 0 ; i < vec1.size (); ++i) {
130+ if (!Equal (el_type1, vec1.Get (i), el_type2, vec2.Get (i))) {
89131 return false ;
90132 }
91- auto const & el_type1 = pt1.array_type ().element_type ();
92- auto const & el_type2 = pt2.array_type ().element_type ();
93- if (el_type1.kind_case () != el_type2.kind_case ()) {
133+ }
134+ return true ;
135+ }
136+
137+ // Compares two sets of Type and Value protos that represent a STRUCT for
138+ // equality.
139+ bool StructEqual ( // NOLINT(misc-no-recursion)
140+ google::bigtable::v2::Type const & pt1,
141+ google::bigtable::v2::Value const & pv1,
142+ google::bigtable::v2::Type const & pt2,
143+ google::bigtable::v2::Value const & pv2) {
144+ auto const & fields1 = pt1.struct_type ().fields ();
145+ auto const & fields2 = pt2.struct_type ().fields ();
146+ if (fields1.size () != fields2.size ()) return false ;
147+ auto const & v1 = pv1.array_value ().values ();
148+ auto const & v2 = pv2.array_value ().values ();
149+ if (fields1.size () != v1.size () || v1.size () != v2.size ()) return false ;
150+ for (int i = 0 ; i < fields1.size (); ++i) {
151+ auto const & f1 = fields1.Get (i);
152+ auto const & f2 = fields2.Get (i);
153+ if (f1.field_name () != f2.field_name ()) return false ;
154+ if (!Equal (f1.type (), v1.Get (i), f2.type (), v2.Get (i))) {
94155 return false ;
95156 }
96- for (int i = 0 ; i < vec1.size (); ++i) {
97- if (!Equal (el_type1, vec1.Get (i), el_type2, vec2.Get (i))) {
98- return false ;
99- }
100- }
101- return true ;
102157 }
103- return false ;
158+ return true ;
104159}
105160
106161// From the proto description, `NULL` values are represented by having a kind
@@ -133,16 +188,17 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
133188 return os << " NULL" ;
134189 }
135190
136- if (v .kind_case () == google::bigtable::v2::Value:: kBoolValue ) {
191+ if (t .kind_case () == google::bigtable::v2::Type:: kBoolType ) {
137192 return os << v.bool_value ();
138193 }
139- if (v .kind_case () == google::bigtable::v2::Value:: kIntValue ) {
194+ if (t .kind_case () == google::bigtable::v2::Type:: kInt64Type ) {
140195 return os << v.int_value ();
141196 }
142- if (v.kind_case () == google::bigtable::v2::Value::kFloatValue ) {
197+ if (t.kind_case () == google::bigtable::v2::Type::kFloat32Type ||
198+ t.kind_case () == google::bigtable::v2::Type::kFloat64Type ) {
143199 return os << v.float_value ();
144200 }
145- if (v .kind_case () == google::bigtable::v2::Value:: kStringValue ) {
201+ if (t .kind_case () == google::bigtable::v2::Type:: kStringType ) {
146202 switch (mode) {
147203 case StreamMode::kScalar :
148204 return os << v.string_value ();
@@ -153,22 +209,22 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
153209 }
154210 return os; // Unreachable, but quiets warning.
155211 }
156- if (v .kind_case () == google::bigtable::v2::Value:: kBytesValue ) {
212+ if (t .kind_case () == google::bigtable::v2::Type:: kBytesType ) {
157213 return os << Bytes (AsString (v.bytes_value ()));
158214 }
159- if (v .kind_case () == google::bigtable::v2::Value:: kTimestampValue ) {
215+ if (t .kind_case () == google::bigtable::v2::Type:: kTimestampType ) {
160216 auto ts = MakeTimestamp (v.timestamp_value ());
161217 if (!ts) {
162218 internal::ThrowStatus (ts.status ());
163219 }
164220 return os << ts.value ();
165221 }
166- if (v .kind_case () == google::bigtable::v2::Value:: kDateValue ) {
222+ if (t .kind_case () == google::bigtable::v2::Type:: kDateType ) {
167223 auto date =
168224 bigtable_internal::FromProto (t, v).get <absl::CivilDay>().value ();
169225 return os << date;
170226 }
171- if (v .kind_case () == google::bigtable::v2::Value:: kArrayValue ) {
227+ if (t .kind_case () == google::bigtable::v2::Type:: kArrayType ) {
172228 char const * delimiter = " " ;
173229 os << ' [' ;
174230 for (auto && val : v.array_value ().values ()) {
@@ -178,15 +234,30 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
178234 delimiter = " , " ;
179235 }
180236 return os << ' ]' ;
181- return os;
237+ }
238+ if (t.kind_case () == google::bigtable::v2::Type::kStructType ) {
239+ char const * delimiter = " " ;
240+ os << ' (' ;
241+ for (int i = 0 ; i < v.array_value ().values_size (); ++i) {
242+ os << delimiter;
243+ if (!t.struct_type ().fields (i).field_name ().empty ()) {
244+ os << ' "' ;
245+ EscapeQuotes (os, t.struct_type ().fields (i).field_name ());
246+ os << ' "' << " : " ;
247+ }
248+ StreamHelper (os, v.array_value ().values (i),
249+ t.struct_type ().fields (i).type (), StreamMode::kAggregate );
250+ delimiter = " , " ;
251+ }
252+ return os << ' )' ;
182253 }
183254 // this should include type name
184- return os << " Error: unknown value type code " ;
255+ return os << " Error: unknown value type code " << t. kind_case () ;
185256}
186257} // namespace
187258
188259bool operator ==(Value const & a, Value const & b) {
189- return Equal (a.type_ , a.value_ , b.type_ , b.value_ );
260+ return bigtable:: Equal (a.type_ , a.value_ , b.type_ , b.value_ );
190261}
191262
192263std::ostream& operator <<(std::ostream& os, Value const & v) {
0 commit comments