Skip to content

Commit

Permalink
feat: add function to get a single row from a range (googleapis/googl…
Browse files Browse the repository at this point in the history
…e-cloud-cpp-spanner#1074)

* feat: add function to get a single row from a range

Fixes: googleapis/google-cloud-cpp-spanner#386

This is just a convenience, but it seems nice in some cases. I found on
example that could use this in our current code. I also have another one
in an upcoming PR where I would like this.
  • Loading branch information
devjgm authored Nov 17, 2019
1 parent 2d4be55 commit 1ce505b
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 8 deletions.
15 changes: 15 additions & 0 deletions google/cloud/spanner/row.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,21 @@ class StreamOf {
iterator end_;
};

/**
* Returns the current row from the given range, if any.
*
* If there are no rows left, an error `Status` is returned. This is a
* convenience function that can be useful if the caller knows there will be
* only a single row returned.
*/
template <typename RowRange>
auto GetCurrentRow(RowRange&& range) -> typename std::decay<
decltype(*std::forward<RowRange>(range).begin())>::type {
auto it = std::forward<RowRange>(range).begin();
if (it != std::forward<RowRange>(range).end()) return *it;
return Status(StatusCode::kResourceExhausted, "No more rows");
}

} // namespace SPANNER_CLIENT_NS
} // namespace spanner
} // namespace cloud
Expand Down
30 changes: 30 additions & 0 deletions google/cloud/spanner/row_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,36 @@ TEST(StreamOf, IterationError) {
EXPECT_EQ(it, end);
}

TEST(GetCurrentRow, BasicEmpty) {
std::vector<Row> rows;
RowRange range(MakeRowStreamIteratorSource(rows));
auto row = GetCurrentRow(range);
EXPECT_FALSE(row.ok());
EXPECT_EQ(row.status().code(), StatusCode::kResourceExhausted);
}

TEST(GetCurrentRow, BasicNotEmpty) {
std::vector<Row> rows;
rows.emplace_back(MakeTestRow({{"num", Value(1)}}));

RowRange range(MakeRowStreamIteratorSource(rows));
auto row = GetCurrentRow(range);
EXPECT_STATUS_OK(row);
EXPECT_EQ(1, *row->get<std::int64_t>(0));
}

TEST(GetCurrentRow, TupleStreamNotEmpty) {
std::vector<Row> rows;
rows.emplace_back(MakeTestRow({{"num", Value(1)}}));

auto row_range = RowRange(MakeRowStreamIteratorSource(rows));
auto tup_range = StreamOf<std::tuple<std::int64_t>>(row_range);

auto row = GetCurrentRow(tup_range);
EXPECT_STATUS_OK(row);
EXPECT_EQ(1, std::get<0>(*row));
}

} // namespace
} // namespace SPANNER_CLIENT_NS
} // namespace spanner
Expand Down
15 changes: 7 additions & 8 deletions google/cloud/spanner/samples/samples.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1001,14 +1001,13 @@ void ReadWriteTransaction(google::cloud::spanner::Client client) {
auto key = spanner::KeySet().AddKey(spanner::MakeKey(singer_id, album_id));
auto rows = client.Read(std::move(txn), "Albums", std::move(key),
{"MarketingBudget"});
for (auto& row : spanner::StreamOf<std::tuple<std::int64_t>>(rows)) {
// Return the error (as opposed to throwing an exception) because
// Commit() only retries on StatusCode::kAborted.
if (!row) return std::move(row).status();
// We expect at most one result from the `Read()` request. Return
// the first one.
return std::get<0>(*std::move(row));
}
// We expect at most one result from the `Read()` request. Return
// the first one.
auto row = GetCurrentRow(spanner::StreamOf<std::tuple<std::int64_t>>(rows));
// Return the error (as opposed to throwing an exception) because
// Commit() only retries on StatusCode::kAborted.
if (!row) return std::move(row).status();
return std::get<0>(*std::move(row));
// Throw an exception because this should terminate the transaction.
throw std::runtime_error("Key not found (" + std::to_string(singer_id) +
"," + std::to_string(album_id) + ")");
Expand Down

0 comments on commit 1ce505b

Please sign in to comment.