Skip to content

Commit 12b7259

Browse files
authored
impl(bigtable): first group of query conformance tests (#15768)
* impl(bigtable): dedup map when creating Value type * impl(bigtable): first group of query conformance tests
1 parent 0f0a7cf commit 12b7259

File tree

9 files changed

+293
-12
lines changed

9 files changed

+293
-12
lines changed

.typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ extend-exclude = [
6060
"docfx/doxygen2toc_test.cc",
6161
# If we're using a post-download patch file for googleapis.
6262
"external/googleapis.patch",
63+
# Mispelled test names in Bigtable conformance tests
64+
"google/cloud/bigtable/ci/run_conformance_tests_proxy_bazel.sh",
6365
]
6466

6567
[default]

google/cloud/bigtable/ci/run_conformance_tests_proxy_bazel.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,43 @@ go test -v \
4646
-skip "Generic_CloseClient|Generic_DeadlineExceeded|NoRetry_OutOfOrderError_Reverse|Retry_LastScannedRow_Reverse|Retry_WithRetryInfo_OverallDedaline|TestExecuteQuery" \
4747
-proxy_addr=:9999
4848
exit_status=$?
49+
50+
# Run all the ExecuteQuery tests that either work or we plan to skip such as
51+
# CloseClient
52+
go test -v \
53+
-run "TestExecuteQuery" \
54+
-skip "CloseClient|FailsOnEmptyMetadata|FailsOnExecuteQueryMetadata|FailsOnInvalidType|FailsOnNotEnoughData|FailsOnNotEnoughDataWithCompleteRows|FailsOnSuccesfulStreamWithNoToken|ChecksumMismatch|FailsOnTypeMismatch|FailsOnTypeMismatchWithinMap|FailsOnTypeMismatchWithinArray|FailsOnTypeMismatchWithinStruct|FailsOnStructMissingField|RetryTest_WithPlanRefresh|PlanRefresh" \
55+
-proxy_addr=:9999
56+
exit_status=$?
57+
58+
# These next four go test commands group the currently failing ExecuteQuery
59+
# tests into groups that exercise similar functionality and should be worked on
60+
# together.
61+
62+
# Metadata tests b/461232934
63+
#go test -v \
64+
# -run "FailsOnEmptyMetadata|FailsOnExecuteQueryMetadata|FailsOnInvalidType" \
65+
# -proxy_addr=:9999
66+
#exit_status=$?
67+
68+
# Stream reading tests b/461232110
69+
#go test -v \
70+
# -run "FailsOnNotEnoughData|FailsOnNotEnoughDataWithCompleteRows|FailsOnSuccesfulStreamWithNoToken|ChecksumMismatch" \
71+
# -proxy_addr=:9999
72+
#exit_status=$?
73+
74+
# Response/Metadata mismatches b/461233335
75+
#go test -v \
76+
# -run "FailsOnTypeMismatch|FailsOnTypeMismatchWithinMap|FailsOnTypeMismatchWithinArray|FailsOnTypeMismatchWithinStruct|FailsOnStructMissingField" \
77+
# -proxy_addr=:9999
78+
#exit_status=$?
79+
80+
# QueryPlan refresh tests b/461233613
81+
#go test -v \
82+
# -run "RetryTest_WithPlanRefresh|PlanRefresh" \
83+
# -proxy_addr=:9999
84+
#exit_status=$?
85+
4986
# Remove the entire module cache, including unpacked source code of versioned
5087
# dependencies.
5188
go clean -modcache

google/cloud/bigtable/test_proxy/cbt_test_proxy.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "google/cloud/bigtable/test_proxy/cbt_test_proxy.h"
1616
#include "google/cloud/bigtable/cell.h"
17+
#include "google/cloud/bigtable/client.h"
1718
#include "google/cloud/bigtable/idempotent_mutation_policy.h"
1819
#include "google/cloud/bigtable/mutations.h"
1920
#include "google/cloud/bigtable/table.h"
@@ -360,6 +361,70 @@ grpc::Status CbtTestProxy::ReadModifyWriteRow(
360361
return grpc::Status();
361362
}
362363

364+
grpc::Status CbtTestProxy::ExecuteQuery(
365+
grpc::ServerContext*,
366+
google::bigtable::testproxy::ExecuteQueryRequest const* request,
367+
google::bigtable::testproxy::ExecuteQueryResult* response) {
368+
// Retrieve connection
369+
auto const& conn = GetConnection(request->client_id());
370+
if (!conn.ok()) return ToGrpcStatus(std::move(conn).status());
371+
auto client = bigtable::Client(*conn);
372+
auto const& request_proto = request->request();
373+
374+
// Call prepare query
375+
auto instance = MakeInstanceResource(request_proto.instance_name());
376+
// NOLINTNEXTLINE(deprecated-declarations)
377+
bigtable::SqlStatement sql_statement{request_proto.query()};
378+
auto prepared_query =
379+
client.PrepareQuery(*std::move(instance), sql_statement);
380+
if (!prepared_query.ok()) {
381+
*response->mutable_status() = ToRpcStatus(prepared_query.status());
382+
return ::grpc::Status();
383+
}
384+
385+
// Bind parameters
386+
std::unordered_map<std::string, Value> params;
387+
for (auto const& param : request_proto.params()) {
388+
auto value =
389+
bigtable_internal::FromProto(param.second.type(), param.second);
390+
params.insert(std::make_pair(param.first, std::move(value)));
391+
}
392+
auto bound_query = prepared_query->BindParameters(params);
393+
auto bound_query_metadata = bound_query.response()->metadata();
394+
395+
RowStream result = client.ExecuteQuery(std::move(bound_query), {});
396+
397+
Status status;
398+
std::vector<google::bigtable::testproxy::SqlRow> proxy_rows;
399+
for (auto& row : result) {
400+
if (!row.ok()) {
401+
status = row.status();
402+
break;
403+
}
404+
google::bigtable::testproxy::SqlRow proxy_row;
405+
for (auto const& v : row->values()) {
406+
*proxy_row.add_values() = bigtable_internal::ToProto(v).second;
407+
}
408+
proxy_rows.push_back(std::move(proxy_row));
409+
}
410+
411+
if (status.ok()) {
412+
for (auto& p : proxy_rows) {
413+
*response->add_rows() = std::move(p);
414+
}
415+
}
416+
417+
// populate metadata
418+
google::bigtable::testproxy::ResultSetMetadata metadata;
419+
for (auto const& column : bound_query_metadata.proto_schema().columns()) {
420+
*metadata.add_columns() = column;
421+
}
422+
*response->mutable_metadata() = metadata;
423+
424+
*response->mutable_status() = ToRpcStatus(status);
425+
return grpc::Status();
426+
}
427+
363428
StatusOr<std::shared_ptr<DataConnection>> CbtTestProxy::GetConnection(
364429
std::string const& client_id) {
365430
std::lock_guard<std::mutex> lk(mu_);

google/cloud/bigtable/test_proxy/cbt_test_proxy.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ class CbtTestProxy final
9191
::google::bigtable::testproxy::ReadModifyWriteRowRequest const* request,
9292
::google::bigtable::testproxy::RowResult* response) override;
9393

94+
grpc::Status ExecuteQuery(
95+
::grpc::ServerContext* context,
96+
::google::bigtable::testproxy::ExecuteQueryRequest const* request,
97+
::google::bigtable::testproxy::ExecuteQueryResult* response) override;
98+
9499
private:
95100
StatusOr<std::shared_ptr<DataConnection>> GetConnection(
96101
std::string const& client_id);

google/cloud/bigtable/value.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "google/cloud/bigtable/value.h"
1616
#include "google/cloud/bigtable/timestamp.h"
1717
#include "google/cloud/internal/throw_delegate.h"
18+
#include "absl/container/flat_hash_set.h"
1819
#include "absl/strings/cord.h"
1920
#include <google/bigtable/v2/types.pb.h>
2021
#include <google/protobuf/descriptor.h>
@@ -591,6 +592,33 @@ StatusOr<absl::CivilDay> Value::GetValue(absl::CivilDay const&,
591592

592593
bool Value::is_null() const { return IsNullValue(value_); }
593594

595+
// We're calling this function from a constructor which we prefer to always
596+
// complete. Any errors encountered will be deferred, and we will report them on
597+
// attempts at accessing the value.
598+
// Any duplicates keys found in the map will be deduped to use the last value
599+
// specified for the key, per:
600+
// https://github.com/googleapis/googleapis/blob/0eeb1be5b78a9c7e006ee57cde95349834ae9f3b/google/bigtable/v2/types.proto#L357
601+
void Value::DedupProtoMap() {
602+
google::bigtable::v2::Value dedup_value;
603+
absl::flat_hash_set<std::string> keys;
604+
for (int i = value_.array_value().values_size() - 1; i >= 0; --i) {
605+
auto map_value_proto = GetProtoValueArrayElement(value_, i);
606+
if (!map_value_proto.has_array_value() ||
607+
map_value_proto.array_value().values_size() != 2) {
608+
continue;
609+
}
610+
auto const& key = map_value_proto.array_value().values(0);
611+
// TODO(#15766): Find a better way to hash these protos.
612+
if (keys.insert(key.SerializeAsString()).second) {
613+
*dedup_value.mutable_array_value()->add_values() =
614+
std::move(map_value_proto);
615+
}
616+
}
617+
std::reverse(dedup_value.mutable_array_value()->mutable_values()->begin(),
618+
dedup_value.mutable_array_value()->mutable_values()->end());
619+
value_ = std::move(dedup_value);
620+
}
621+
594622
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
595623
} // namespace bigtable
596624
} // namespace cloud

google/cloud/bigtable/value.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -629,13 +629,6 @@ class Value {
629629
pt.map_type().value_type());
630630
if (!key) return std::move(key).status();
631631
if (!value) return std::move(value).status();
632-
633-
// the documented behavior indicates that the last value will take
634-
// precedence for a given key.
635-
auto const& pos = m.find(key.value());
636-
if (pos != m.end()) {
637-
m.erase(pos);
638-
}
639632
m.insert(std::make_pair(*std::move(key), *std::move(value)));
640633
}
641634
return m;
@@ -692,6 +685,8 @@ class Value {
692685
return std::move(*pv.mutable_array_value()->mutable_values(pos));
693686
}
694687

688+
void DedupProtoMap();
689+
695690
// A private templated constructor that is called by all the public
696691
// constructors to set the type_ and value_ members. The `PrivateConstructor`
697692
// type is used so that this overload is never chosen for
@@ -701,10 +696,20 @@ class Value {
701696
struct PrivateConstructor {};
702697
template <typename T>
703698
Value(PrivateConstructor, T&& t)
704-
: type_(MakeTypeProto(t)), value_(MakeValueProto(std::forward<T>(t))) {}
699+
: type_(MakeTypeProto(t)), value_(MakeValueProto(std::forward<T>(t))) {
700+
if (type_.has_map_type() && value_.has_array_value() &&
701+
!value_.array_value().values().empty()) {
702+
DedupProtoMap();
703+
}
704+
}
705705

706706
Value(google::bigtable::v2::Type t, google::bigtable::v2::Value v)
707-
: type_(std::move(t)), value_(std::move(v)) {}
707+
: type_(std::move(t)), value_(std::move(v)) {
708+
if (type_.has_map_type() && value_.has_array_value() &&
709+
!value_.array_value().values().empty()) {
710+
DedupProtoMap();
711+
}
712+
}
708713

709714
friend struct bigtable_internal::ValueInternals;
710715
friend class Parameter;

google/cloud/bigtable/value_test.cc

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "google/cloud/testing_util/is_proto_equal.h"
1919
#include "google/cloud/testing_util/status_matchers.h"
2020
#include "absl/strings/cord.h"
21+
#include <google/protobuf/text_format.h>
2122
#include <google/type/date.pb.h>
2223
#include <gmock/gmock.h>
2324
#include <cmath>
@@ -39,6 +40,7 @@ namespace {
3940
using ::google::cloud::testing_util::IsOk;
4041
using ::google::cloud::testing_util::IsOkAndHolds;
4142
using ::google::cloud::testing_util::IsProtoEqual;
43+
using ::google::protobuf::TextFormat;
4244
using ::testing::Not;
4345

4446
absl::Time MakeTime(std::int64_t sec, int nanos) {
@@ -169,8 +171,6 @@ TEST(Value, BasicSemantics) {
169171
for (auto ts : TestTimes()) {
170172
SCOPED_TRACE("Testing: google::cloud::bigtable::Timestamp " +
171173
bigtable_internal::TimestampToRFC3339(ts));
172-
std::cout << "Testing: google::cloud::bigtable::Timestamp "
173-
<< bigtable_internal::TimestampToRFC3339(ts) << std::endl;
174174
TestBasicSemantics(ts);
175175
std::vector<Timestamp> v(5, ts);
176176
TestBasicSemantics(v);
@@ -1004,6 +1004,56 @@ TEST(Value, ProtoConversionMap) {
10041004
EXPECT_TRUE(p.first.has_map_type());
10051005
}
10061006

1007+
TEST(Value, ProtoMapWithDuplicateKeys) {
1008+
auto constexpr kTypeProto = R"""(
1009+
map_type {
1010+
key_type {
1011+
bytes_type {
1012+
}
1013+
}
1014+
value_type {
1015+
string_type {
1016+
}
1017+
}
1018+
}
1019+
)""";
1020+
google::bigtable::v2::Type type_proto;
1021+
ASSERT_TRUE(TextFormat::ParseFromString(kTypeProto, &type_proto));
1022+
1023+
auto constexpr kValueProto = R"""(
1024+
array_value {
1025+
values {
1026+
array_value {
1027+
values {
1028+
bytes_value: "foo"
1029+
}
1030+
values {
1031+
string_value: "foo"
1032+
}
1033+
}
1034+
}
1035+
values {
1036+
array_value {
1037+
values {
1038+
bytes_value: "foo"
1039+
}
1040+
values {
1041+
string_value: "bar"
1042+
}
1043+
}
1044+
}
1045+
}
1046+
)""";
1047+
google::bigtable::v2::Value value_proto;
1048+
ASSERT_TRUE(TextFormat::ParseFromString(kValueProto, &value_proto));
1049+
1050+
auto value = bigtable_internal::FromProto(type_proto, value_proto);
1051+
auto map = value.get<std::unordered_map<Bytes, std::string>>();
1052+
ASSERT_STATUS_OK(map);
1053+
EXPECT_THAT(*map, ::testing::UnorderedElementsAre(
1054+
::testing::Pair(Bytes("foo"), "bar")));
1055+
}
1056+
10071057
void SetNullProtoKind(Value& v) {
10081058
auto p = bigtable_internal::ToProto(v);
10091059
p.second.clear_kind();

protos/google/cloud/bigtable/test_proxy/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ proto_library(
3232
"@com_google_googleapis//google/api:client_proto",
3333
"@com_google_googleapis//google/bigtable/v2:bigtable_proto",
3434
"@com_google_googleapis//google/rpc:status_proto",
35+
"@com_google_protobuf//:descriptor_proto",
3536
"@com_google_protobuf//:duration_proto",
3637
],
3738
)

0 commit comments

Comments
 (0)