From 1b4a72de145fed8ba461d4b80d8de47f16691672 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 25 Jun 2015 21:49:21 +0200 Subject: [PATCH 01/12] query: Introduce query::max_rows --- query-request.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/query-request.hh b/query-request.hh index 7f25bc96c3..cccc934ef6 100644 --- a/query-request.hh +++ b/query-request.hh @@ -241,6 +241,8 @@ public: friend std::ostream& operator<<(std::ostream& out, const partition_slice& ps); }; +constexpr auto max_rows = std::numeric_limits::max(); + // Full specification of a query to the database. // Intended for passing across replicas. // Can be accessed across cores. @@ -250,7 +252,7 @@ public: partition_slice slice; uint32_t row_limit; public: - read_command(const utils::UUID& cf_id, partition_slice slice, uint32_t row_limit) + read_command(const utils::UUID& cf_id, partition_slice slice, uint32_t row_limit = max_rows) : cf_id(cf_id) , slice(std::move(slice)) , row_limit(row_limit) From 3fc951e807273b86082ec6337712e5fcffa7042a Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 25 Jun 2015 21:49:08 +0200 Subject: [PATCH 02/12] mutation_partition: Use default value for row_limit in query() --- database.cc | 2 +- mutation_partition.cc | 6 +++--- mutation_partition.hh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/database.cc b/database.cc index 31231d23ce..4ea9e32d10 100644 --- a/database.cc +++ b/database.cc @@ -1019,7 +1019,7 @@ column_family::query(const query::read_command& cmd, const std::vectorkey()); - mo->partition().query(*_schema, qs.cmd.slice, qs.limit, p_builder); + mo->partition().query(p_builder, *_schema, qs.cmd.slice, qs.limit); p_builder.finish(); qs.limit -= p_builder.row_count(); } else { diff --git a/mutation_partition.cc b/mutation_partition.cc index ea7b693007..6fe0b5856a 100644 --- a/mutation_partition.cc +++ b/mutation_partition.cc @@ -270,10 +270,10 @@ bool has_any_live_data(const row& cells, tombstone tomb, ColumnDefResolver&& id_ } void -mutation_partition::query(const schema& s, +mutation_partition::query(query::result::partition_writer& pw, + const schema& s, const query::partition_slice& slice, - uint32_t limit, - query::result::partition_writer& pw) const + uint32_t limit) const { auto regular_column_resolver = [&s] (column_id id) -> const column_definition& { return s.regular_column_at(id); diff --git a/mutation_partition.hh b/mutation_partition.hh index 651f123c69..7b50f601f0 100644 --- a/mutation_partition.hh +++ b/mutation_partition.hh @@ -273,5 +273,5 @@ public: tombstone tombstone_for_row(const schema& schema, const rows_entry& e) const; boost::iterator_range range(const schema& schema, const query::range& r) const; // Returns at most "limit" rows. The limit must be greater than 0. - void query(const schema& s, const query::partition_slice& slice, uint32_t limit, query::result::partition_writer& pw) const; + void query(query::result::partition_writer& pw, const schema& s, const query::partition_slice& slice, uint32_t limit = query::max_rows) const; }; From f46b7a815e8d9c1ec4058407ab1399c22a03ec8d Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Fri, 26 Jun 2015 13:14:15 +0200 Subject: [PATCH 03/12] query: Fix typos in comments --- query-result.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/query-result.hh b/query-result.hh index 6a7a26aeb7..c542868553 100644 --- a/query-result.hh +++ b/query-result.hh @@ -33,7 +33,7 @@ public: }; // -// The query results are stored in a serialized from. This is in order to +// The query results are stored in a serialized form. This is in order to // address the following problems, which a structured format has: // // - high level of indirection (vector of vectors of vectors of blobs), which @@ -62,7 +62,7 @@ public: // pass the data using zero-copy to the client, prepending a header. // // Users which need more complex structure of query results, should -// trasnform it to such using appropriate visitors. +// transform it to such using appropriate visitors. // TODO: insert reference to such visitors here. // // Query results have dynamic format. In some queries (maybe even in typical From c9e5508e3cca5d41595a422cd0fc7fef8838095e Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Fri, 26 Jun 2015 13:10:44 +0200 Subject: [PATCH 04/12] result_set_builder: Make build() return unwrapped object It's better to let the user decide which kind (if any) of smart pointer to wrap it into. --- db/system_keyspace.cc | 2 +- query-result-set.cc | 4 ++-- query-result-set.hh | 2 +- service/storage_proxy.cc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/db/system_keyspace.cc b/db/system_keyspace.cc index 5d11c623fc..24e07606f5 100644 --- a/db/system_keyspace.cc +++ b/db/system_keyspace.cc @@ -1081,7 +1081,7 @@ query(service::storage_proxy& proxy, const sstring& cf_name) { bytes_ostream w(result->buf()); query::result_view view(w.linearize()); view.consume(cmd->slice, builder); - return builder.build(); + return make_lw_shared(builder.build()); }); } diff --git a/query-result-set.cc b/query-result-set.cc index 0e62fb6ddc..8f7e5b0757 100644 --- a/query-result-set.cc +++ b/query-result-set.cc @@ -26,8 +26,8 @@ result_set_builder::result_set_builder(schema_ptr schema) : _schema{schema} { } -lw_shared_ptr result_set_builder::build() const { - return make_lw_shared(_rows); +result_set result_set_builder::build() const { + return { _rows }; } void result_set_builder::accept_new_partition(const partition_key& key, uint32_t row_count) diff --git a/query-result-set.hh b/query-result-set.hh index e2c3f1dbb3..4051c48a23 100644 --- a/query-result-set.hh +++ b/query-result-set.hh @@ -111,7 +111,7 @@ class result_set_builder { std::unordered_map _pkey_cells; public: result_set_builder(schema_ptr schema); - lw_shared_ptr build() const; + result_set build() const; void accept_new_partition(const partition_key& key, uint32_t row_count); void accept_new_partition(uint32_t row_count); void accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row); diff --git a/service/storage_proxy.cc b/service/storage_proxy.cc index af4d9bf624..456ce91df0 100644 --- a/service/storage_proxy.cc +++ b/service/storage_proxy.cc @@ -1624,7 +1624,7 @@ storage_proxy::query_local(const sstring& ks_name, const sstring& cf_name, const bytes_ostream w(result->buf()); query::result_view view(w.linearize()); view.consume(slice, builder); - return builder.build(); + return make_lw_shared(builder.build()); }); } From a1f6dec067b27e1f7303b6f95b2a8ce1c989b96f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Fri, 26 Jun 2015 13:12:27 +0200 Subject: [PATCH 05/12] result_set: Introduce from_raw_result() factory method --- db/system_keyspace.cc | 6 +----- query-result-set.cc | 18 ++++++++++++++++++ query-result-set.hh | 1 + service/storage_proxy.cc | 6 +----- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/db/system_keyspace.cc b/db/system_keyspace.cc index 24e07606f5..726ef2f93a 100644 --- a/db/system_keyspace.cc +++ b/db/system_keyspace.cc @@ -1077,11 +1077,7 @@ query(service::storage_proxy& proxy, const sstring& cf_name) { query::partition_slice slice{{query::clustering_range::make_open_ended_both_sides()}, static_cols, regular_cols, opts}; auto cmd = make_lw_shared(schema->id(), slice, std::numeric_limits::max()); return proxy.query(cmd, {query::full_partition_range}, db::consistency_level::ONE).then([schema, cmd] (auto&& result) { - query::result_set_builder builder{schema}; - bytes_ostream w(result->buf()); - query::result_view view(w.linearize()); - view.consume(cmd->slice, builder); - return make_lw_shared(builder.build()); + return make_lw_shared(query::result_set::from_raw_result(schema, cmd->slice, *result)); }); } diff --git a/query-result-set.cc b/query-result-set.cc index 8f7e5b0757..35dd8f08c3 100644 --- a/query-result-set.cc +++ b/query-result-set.cc @@ -119,4 +119,22 @@ result_set_builder::deserialize(const result_row_view& row, bool is_static) return cells; } +result_set +result_set::from_raw_result(schema_ptr s, const partition_slice& slice, const result& r) { + auto make = [&slice, s = std::move(s)] (bytes_view v) mutable { + result_set_builder builder{std::move(s)}; + result_view view(v); + view.consume(slice, builder); + return builder.build(); + }; + + if (r.buf().is_linearized()) { + return make(r.buf().view()); + } else { + // FIXME: make result_view::consume() work on fragments to avoid linearization. + bytes_ostream w(r.buf()); + return make(w.linearize()); + } +} + } diff --git a/query-result-set.hh b/query-result-set.hh index 4051c48a23..b061090d28 100644 --- a/query-result-set.hh +++ b/query-result-set.hh @@ -79,6 +79,7 @@ inline bool operator!=(const result_set_row& x, const result_set_row& y) { class result_set { std::vector _rows; public: + static result_set from_raw_result(schema_ptr, const partition_slice&, const result&); result_set(const std::vector& rows) : _rows{std::move(rows)} { } diff --git a/service/storage_proxy.cc b/service/storage_proxy.cc index 456ce91df0..d2127319a2 100644 --- a/service/storage_proxy.cc +++ b/service/storage_proxy.cc @@ -1620,11 +1620,7 @@ storage_proxy::query_local(const sstring& ks_name, const sstring& cf_name, const }); }); }).then([this, schema, slice] (auto&& result) { - query::result_set_builder builder{schema}; - bytes_ostream w(result->buf()); - query::result_view view(w.linearize()); - view.consume(slice, builder); - return make_lw_shared(builder.build()); + return make_lw_shared(query::result_set::from_raw_result(schema, slice, *result)); }); } From f388139a7b3e50f8648427cc886b2bf1855830ab Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 29 Jun 2015 10:17:40 +0200 Subject: [PATCH 06/12] result_set_builder: Move to source file --- query-result-set.cc | 22 ++++++++++++++++++++++ query-result-set.hh | 25 +++---------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/query-result-set.cc b/query-result-set.cc index 35dd8f08c3..781136b1ca 100644 --- a/query-result-set.cc +++ b/query-result-set.cc @@ -3,9 +3,31 @@ */ #include "query-result-set.hh" +#include "query-result-reader.hh" namespace query { +// Result set builder is passed as a visitor to query_result::consume() +// function. You can call the build() method to obtain a result set that +// contains cells from the visited results. +class result_set_builder { + schema_ptr _schema; + std::vector _rows; + std::unordered_map _pkey_cells; +public: + result_set_builder(schema_ptr schema); + result_set build() const; + void accept_new_partition(const partition_key& key, uint32_t row_count); + void accept_new_partition(uint32_t row_count); + void accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row); + void accept_new_row(const result_row_view &static_row, const result_row_view &row); + void accept_partition_end(const result_row_view& static_row); +private: + std::unordered_map deserialize(const partition_key& key); + std::unordered_map deserialize(const clustering_key& key); + std::unordered_map deserialize(const result_row_view& row, bool is_static); +}; + std::ostream& operator<<(std::ostream& out, const result_set_row& row) { for (auto&& cell : row._cells) { auto&& type = cell.second.type(); diff --git a/query-result-set.hh b/query-result-set.hh index b061090d28..c509bf037b 100644 --- a/query-result-set.hh +++ b/query-result-set.hh @@ -4,9 +4,11 @@ #pragma once -#include "query-result-reader.hh" #include "core/shared_ptr.hh" +#include "query-request.hh" +#include "query-result.hh" +#include "schema.hh" #include #include @@ -103,25 +105,4 @@ inline bool operator==(const result_set& x, const result_set& y) { return x._rows == y._rows; } -// Result set builder is passed as a visitor to query_result::consume() -// function. You can call the build() method to obtain a result set that -// contains cells from the visited results. -class result_set_builder { - schema_ptr _schema; - std::vector _rows; - std::unordered_map _pkey_cells; -public: - result_set_builder(schema_ptr schema); - result_set build() const; - void accept_new_partition(const partition_key& key, uint32_t row_count); - void accept_new_partition(uint32_t row_count); - void accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row); - void accept_new_row(const result_row_view &static_row, const result_row_view &row); - void accept_partition_end(const result_row_view& static_row); -private: - std::unordered_map deserialize(const partition_key& key); - std::unordered_map deserialize(const clustering_key& key); - std::unordered_map deserialize(const result_row_view& row, bool is_static); -}; - } From 4c008e059afd0e2daceadfefa1db5f9170207604 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 29 Jun 2015 13:11:45 +0200 Subject: [PATCH 07/12] result_set: Store schema pointer with result_set --- query-result-set.cc | 2 +- query-result-set.hh | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/query-result-set.cc b/query-result-set.cc index 781136b1ca..7e9730f720 100644 --- a/query-result-set.cc +++ b/query-result-set.cc @@ -49,7 +49,7 @@ result_set_builder::result_set_builder(schema_ptr schema) { } result_set result_set_builder::build() const { - return { _rows }; + return { _schema, _rows }; } void result_set_builder::accept_new_partition(const partition_key& key, uint32_t row_count) diff --git a/query-result-set.hh b/query-result-set.hh index c509bf037b..397c34976f 100644 --- a/query-result-set.hh +++ b/query-result-set.hh @@ -79,11 +79,12 @@ inline bool operator!=(const result_set_row& x, const result_set_row& y) { // deserialized format. To obtain a result set, use the result_set_builder // class as a visitor to query_result::consume() function. class result_set { + schema_ptr _schema; std::vector _rows; public: static result_set from_raw_result(schema_ptr, const partition_slice&, const result&); - result_set(const std::vector& rows) - : _rows{std::move(rows)} + result_set(schema_ptr s, const std::vector& rows) + : _schema(std::move(s)), _rows{std::move(rows)} { } bool empty() const { return _rows.empty(); @@ -97,6 +98,9 @@ public: const std::vector& rows() const { return _rows; } + const schema_ptr& schema() const { + return _schema; + } friend inline bool operator==(const result_set& x, const result_set& y); friend std::ostream& operator<<(std::ostream& out, const result_set& rs); }; From c5d1f9c493409ad4afc6a52ee24499c3954840e1 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 29 Jun 2015 13:12:20 +0200 Subject: [PATCH 08/12] result_set: Expose get_data_value() --- query-result-set.hh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/query-result-set.hh b/query-result-set.hh index 397c34976f..9f35eca9d4 100644 --- a/query-result-set.hh +++ b/query-result-set.hh @@ -41,14 +41,19 @@ public: return _cells.count(column_name) > 0; } // Look up a deserialized row cell value by column name. - template - std::experimental::optional - get(const sstring& column_name) const throw (no_such_column) { + const data_value& + get_data_value(const sstring& column_name) const throw (no_such_column) { auto it = _cells.find(column_name); if (it == _cells.end()) { throw no_such_column(column_name); } - auto&& value = it->second.value(); + return it->second; + } + // Look up a deserialized row cell value by column name. + template + std::experimental::optional + get(const sstring& column_name) const throw (no_such_column) { + auto&& value = get_data_value(column_name).value(); if (value.empty()) { return std::experimental::nullopt; } From eba6b492a6d24cde50176da625f0922fdc123c48 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 29 Jun 2015 13:14:18 +0200 Subject: [PATCH 09/12] tests: Introduce result_set assertions --- configure.py | 1 + tests/urchin/result_set_assertions.cc | 73 +++++++++++++++++++++++++++ tests/urchin/result_set_assertions.hh | 51 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 tests/urchin/result_set_assertions.cc create mode 100644 tests/urchin/result_set_assertions.hh diff --git a/configure.py b/configure.py index 575ea2aee2..c5c9383fa4 100755 --- a/configure.py +++ b/configure.py @@ -509,6 +509,7 @@ urchin_core = (['database.cc', urchin_tests_dependencies = urchin_core + http + api + [ 'tests/urchin/cql_test_env.cc', 'tests/urchin/cql_assertions.cc', + 'tests/urchin/result_set_assertions.cc', ] deps = { diff --git a/tests/urchin/result_set_assertions.cc b/tests/urchin/result_set_assertions.cc new file mode 100644 index 0000000000..1bdebf2efc --- /dev/null +++ b/tests/urchin/result_set_assertions.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Cloudius Systems + */ + +#include + +#include "result_set_assertions.hh" +#include "to_string.hh" + +static inline +sstring to_sstring(const bytes& b) { + return sstring(b.begin(), b.end()); +} + +bool +row_assertion::matches(const query::result_set_row& row) const { + for (auto&& column_and_value : _expected_values) { + auto&& name = column_and_value.first; + auto&& value = column_and_value.second; + + // FIXME: result_set_row works on sstring column names instead of more general "bytes". + auto ss_name = to_sstring(name); + + if (!row.has(ss_name)) { + return false; + } + const data_value& val = row.get_data_value(ss_name); + if (val != data_value(boost::any(value), val.type())) { + return false; + } + } + return true; +} + +sstring +row_assertion::describe(schema_ptr schema) const { + return "{" + ::join(", ", _expected_values | boost::adaptors::transformed([&schema] (auto&& e) { + auto&& name = e.first; + auto&& value = e.second; + const column_definition* def = schema->get_column_definition(name); + if (!def) { + BOOST_FAIL(sprint("Schema is missing column definition for '%s'", name)); + } + return sprint("%s=\"%s\"", to_sstring(name), def->type->to_string(def->type->decompose(value))); + })) + "}"; +} + +const result_set_assertions& +result_set_assertions::has(const row_assertion& ra) const { + for (auto&& row : _rs.rows()) { + if (ra.matches(row)) { + return *this; + } + } + BOOST_FAIL(sprint("Row %s not found in %s", ra.describe(_rs.schema()), _rs)); + return *this; +} + +const result_set_assertions& +result_set_assertions::has_only(const row_assertion& ra) const { + BOOST_REQUIRE(_rs.rows().size() == 1); + auto& row = _rs.rows()[0]; + if (!ra.matches(row)) { + BOOST_FAIL(sprint("Expected %s but got %s", ra.describe(_rs.schema()), row)); + } + return *this; +} + +const result_set_assertions& +result_set_assertions::is_empty() const { + BOOST_REQUIRE_EQUAL(_rs.rows().size(), 0); + return *this; +} diff --git a/tests/urchin/result_set_assertions.hh b/tests/urchin/result_set_assertions.hh new file mode 100644 index 0000000000..6d04cb4685 --- /dev/null +++ b/tests/urchin/result_set_assertions.hh @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Cloudius Systems + */ + +#pragma once + +#include + +#include "query-result-set.hh" + +// +// Contains assertions for query::result_set objects +// +// Example use: +// +// assert_that(rs) +// .has(a_row().with_column("column_name", "value")); +// + +class row_assertion { + std::map _expected_values; +public: + row_assertion& with_column(bytes name, boost::any value) { + _expected_values.emplace(name, value); + return *this; + } +private: + friend class result_set_assertions; + bool matches(const query::result_set_row& row) const; + sstring describe(schema_ptr s) const; +}; + +inline +row_assertion a_row() { + return {}; +} + +class result_set_assertions { + const query::result_set& _rs; +public: + result_set_assertions(const query::result_set& rs) : _rs(rs) { } + const result_set_assertions& has(const row_assertion& ra) const; + const result_set_assertions& has_only(const row_assertion& ra) const; + const result_set_assertions& is_empty() const; +}; + +// Make rs live as long as the returned assertion object is used +inline +result_set_assertions assert_that(const query::result_set& rs) { + return { rs }; +} From f711e4e9787315a1d2ed5c51117825518dd80055 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 29 Jun 2015 13:14:59 +0200 Subject: [PATCH 10/12] tests: Mark assert_that() inline instead of static --- tests/urchin/mutation_reader_assertions.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/urchin/mutation_reader_assertions.hh b/tests/urchin/mutation_reader_assertions.hh index 8a5f47c0c6..d06bc054c2 100644 --- a/tests/urchin/mutation_reader_assertions.hh +++ b/tests/urchin/mutation_reader_assertions.hh @@ -33,6 +33,7 @@ public: } }; -static reader_assertions assert_that(mutation_reader r) { +inline +reader_assertions assert_that(mutation_reader r) { return { std::move(r) }; } From 464434d3d46499b838960b8b5c1371b2e858aa31 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Fri, 26 Jun 2015 13:14:56 +0200 Subject: [PATCH 11/12] mutation: Add query() method --- mutation.cc | 9 +++++++++ mutation.hh | 2 ++ 2 files changed, 11 insertions(+) diff --git a/mutation.cc b/mutation.cc index eb0456c7ef..fbb9a6a5fa 100644 --- a/mutation.cc +++ b/mutation.cc @@ -98,3 +98,12 @@ bool mutation::operator==(const mutation& m) const { bool mutation::operator!=(const mutation& m) const { return !(*this == m); } + +query::result +mutation::query(const query::partition_slice& slice, uint32_t row_limit) const { + query::result::builder builder(slice); + auto pb = builder.add_partition(key()); + _p.query(pb, *_schema, slice, row_limit); + pb.finish(); + return builder.build(); +} diff --git a/mutation.hh b/mutation.hh index 806fffcd84..c5d8af6b45 100644 --- a/mutation.hh +++ b/mutation.hh @@ -41,6 +41,8 @@ public: const utils::UUID& column_family_id() const { return _schema->id(); } bool operator==(const mutation&) const; bool operator!=(const mutation&) const; +public: + query::result query(const query::partition_slice&, uint32_t row_limit = query::max_rows) const; private: friend std::ostream& operator<<(std::ostream& os, const mutation& m); }; From f89ae4078f92c98f9c8532939e2a78167fd36edf Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 2 Jul 2015 10:41:46 +0200 Subject: [PATCH 12/12] tests: Add test for querying a mutation --- tests/urchin/mutation_test.cc | 71 +++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/tests/urchin/mutation_test.cc b/tests/urchin/mutation_test.cc index 50b672809c..622c251f27 100644 --- a/tests/urchin/mutation_test.cc +++ b/tests/urchin/mutation_test.cc @@ -4,12 +4,26 @@ #define BOOST_TEST_DYN_LINK -#include "tests/test-utils.hh" +#include +#include +#include +#include + #include "core/sstring.hh" +#include "core/do_with.hh" +#include "core/thread.hh" + #include "database.hh" #include "utils/UUID_gen.hh" -#include "core/do_with.hh" -#include +#include "mutation_reader.hh" +#include "schema_builder.hh" +#include "query-result-set.hh" +#include "query-result-reader.hh" + +#include "tests/test-utils.hh" +#include "tests/urchin/mutation_assertions.hh" +#include "tests/urchin/mutation_reader_assertions.hh" +#include "tests/urchin/result_set_assertions.hh" static sstring some_keyspace("ks"); static sstring some_column_family("cf"); @@ -383,3 +397,54 @@ SEASTAR_TEST_CASE(test_cell_ordering) { atomic_cell::make_dead(1, expiry_2)); return make_ready_future<>(); } + +static query::partition_slice make_full_slice(const schema& s) { + query::partition_slice::option_set options; + options.set(); + options.set(); + options.set(); + + std::vector ranges; + ranges.emplace_back(query::clustering_range::make_open_ended_both_sides()); + + std::vector static_columns; + boost::range::push_back(static_columns, + s.static_columns() | boost::adaptors::transformed(std::mem_fn(&column_definition::id))); + + std::vector regular_columns; + boost::range::push_back(regular_columns, + s.regular_columns() | boost::adaptors::transformed(std::mem_fn(&column_definition::id))); + + return { + std::move(ranges), + std::move(static_columns), + std::move(regular_columns), + std::move(options) + }; +} + +SEASTAR_TEST_CASE(test_querying_of_mutation) { + return seastar::async([] { + auto s = schema_builder("ks", "cf") + .with_column("pk", bytes_type, column_kind::partition_key) + .with_column("v", bytes_type, column_kind::regular_column) + .build(); + + auto resultify = [s] (const mutation& m) -> query::result_set { + auto slice = make_full_slice(*s); + return query::result_set::from_raw_result(s, slice, m.query(slice)); + }; + + mutation m(partition_key::from_single_value(*s, "key1"), s); + m.set_clustered_cell(clustering_key::make_empty(*s), "v", bytes("v1"), 1); + + assert_that(resultify(m)) + .has_only(a_row() + .with_column("pk", bytes("key1")) + .with_column("v", bytes("v1"))); + + m.partition().apply(tombstone(2, gc_clock::now())); + + assert_that(resultify(m)).is_empty(); + }); +}