@@ -1113,6 +1113,74 @@ TEST_P(DataIntegrationTest, MultiColumnQuery) {
11131113 RowType (" multi-column-query-row-2" , " v21" , " v22" )));
11141114}
11151115
1116+ TEST_P (DataIntegrationTest, QueryWithNulls) {
1117+ if (UsingCloudBigtableEmulator ()) GTEST_SKIP ();
1118+ auto const table_id = testing::TableTestEnvironment::table_id ();
1119+ auto retry_policy_option = DataLimitedErrorCountRetryPolicy (0 ).clone ();
1120+ auto backoff_policy_option =
1121+ google::cloud::internal::ExponentialBackoffPolicy (ms (0 ), ms (0 ), 2.0 )
1122+ .clone ();
1123+ auto query_refresh_option =
1124+ bigtable::experimental::QueryPlanRefreshLimitedErrorCountRetryPolicy (0 )
1125+ .clone ();
1126+ auto opts =
1127+ Options{}
1128+ .set <DataRetryPolicyOption>(std::move (retry_policy_option))
1129+ .set <DataBackoffPolicyOption>(std::move (backoff_policy_option))
1130+ .set <bigtable::experimental::QueryPlanRefreshRetryPolicyOption>(
1131+ std::move (query_refresh_option));
1132+ auto connection = google::cloud::bigtable::MakeDataConnection (opts);
1133+ auto table =
1134+ Table (connection, TableResource (project_id (), instance_id (), table_id));
1135+ std::string const row_key1 = " query-with-nulls-row-1" ;
1136+ std::string const row_key2 = " query-with-nulls-row-2" ;
1137+ std::string const row_key3 = " query-with-nulls-row-3" ;
1138+ std::string const family = kFamily4 ;
1139+ std::string const column1 = " c1" ;
1140+ std::string const column2 = " c2" ;
1141+ std::string const value1 = " v1" ;
1142+ std::string const value3 = " v3" ;
1143+
1144+ // Create row_key2 with no value for column 1.
1145+ std::vector<Cell> created = {
1146+ {row_key1, family, column1, 0 , value1},
1147+ {row_key2, family, column2, 0 , " " },
1148+ {row_key3, family, column1, 0 , value3},
1149+ };
1150+ BulkApply (table, created);
1151+
1152+ auto client = Client (connection, opts);
1153+ std::vector<std::string> full_table_path =
1154+ absl::StrSplit (table.table_name (), ' /' );
1155+ auto table_name = full_table_path.back ();
1156+ std::string quoted_table_name = " `" + table_name + " `" ;
1157+ Project project (project_id ());
1158+ InstanceResource instance_resource (project, instance_id ());
1159+ auto prepared_query = client.PrepareQuery (
1160+ instance_resource,
1161+ SqlStatement (" SELECT CAST(_key AS STRING) AS _key, "
1162+ " CAST(family4['c1'] AS STRING) AS c1 FROM " +
1163+ quoted_table_name + " WHERE _key IN ('" + row_key1 + " ', '" +
1164+ row_key2 + " ', '" + row_key3 + " ')" ));
1165+ ASSERT_STATUS_OK (prepared_query);
1166+ auto bound_query = prepared_query->BindParameters ({});
1167+ auto row_stream = client.ExecuteQuery (std::move (bound_query));
1168+
1169+ using RowType = std::tuple<std::string, absl::optional<std::string>>;
1170+ std::vector<RowType> actual_rows;
1171+ for (auto & row : StreamOf<RowType>(row_stream)) {
1172+ ASSERT_STATUS_OK (row);
1173+ actual_rows.push_back (*std::move (row));
1174+ }
1175+ EXPECT_EQ (actual_rows.size (), 3 );
1176+ EXPECT_THAT (
1177+ actual_rows,
1178+ UnorderedElementsAre (
1179+ std::make_tuple (row_key1, absl::optional<std::string>(value1)),
1180+ std::make_tuple (row_key2, absl::optional<std::string>(absl::nullopt )),
1181+ std::make_tuple (row_key3, absl::optional<std::string>(value3))));
1182+ }
1183+
11161184// TODO(#8800) - remove after deprecation is complete
11171185#include " google/cloud/internal/diagnostics_pop.inc"
11181186
0 commit comments