From afa438d60d4fd2e67450f968cf83a9c3eeaebb63 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 12 Feb 2026 11:59:01 +0200 Subject: [PATCH 1/4] types: data_value: assert is_nothrow_move_constructible and assignable Signed-off-by: Benny Halevy --- types/types.cc | 6 +++++- types/types.hh | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/types/types.cc b/types/types.cc index 9e49b19711..d18a0e840f 100644 --- a/types/types.cc +++ b/types/types.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "utils/big_decimal.hh" #include "utils/date.h" @@ -55,6 +56,9 @@ #include "types/set.hh" #include "types/listlike_partial_deserializing_iterator.hh" +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); + static logging::logger tlogger("types"); bytes_view_opt read_collection_value(bytes_view& in); @@ -3799,7 +3803,7 @@ data_value::data_value(const data_value& v) : _value(nullptr), _type(v._type) { } data_value& -data_value::operator=(data_value&& x) { +data_value::operator=(data_value&& x) noexcept { auto tmp = std::move(x); std::swap(tmp._value, this->_value); std::swap(tmp._type, this->_type); diff --git a/types/types.hh b/types/types.hh index f13a7767de..fd89b35d83 100644 --- a/types/types.hh +++ b/types/types.hh @@ -261,7 +261,7 @@ public: data_value(bool_class); data_value& operator=(const data_value&); - data_value& operator=(data_value&&); + data_value& operator=(data_value&&) noexcept; const data_type& type() const { return _type; } From c0607110c41bcfd76f0e967d43b24f54ea68599f Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 12 Feb 2026 12:01:03 +0200 Subject: [PATCH 2/4] query: non_null_data_value: assert is_nothrow_move_constructible and assignable To be used later in query result_set{row,} and friends. Signed-off-by: Benny Halevy --- query/query-result-set.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/query/query-result-set.cc b/query/query-result-set.cc index cfd3f650ef..4ce29ddc09 100644 --- a/query/query-result-set.cc +++ b/query/query-result-set.cc @@ -17,6 +17,9 @@ namespace query { +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); + class deserialization_error : public std::runtime_error { public: using runtime_error::runtime_error; From b433a5bcf85995099fd92e85ddafabbafec279c7 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 12 Feb 2026 12:01:03 +0200 Subject: [PATCH 3/4] query: result_set_row: make noexcept Remove const specifier from result_set_row._cells member to make the class nothrow_move_constructible and nothrow_move_assignable To be used later in query result_set and friends. Signed-off-by: Benny Halevy --- query/query-result-set.cc | 4 +++- query/query-result-set.hh | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/query/query-result-set.cc b/query/query-result-set.cc index 4ce29ddc09..5ebe174eec 100644 --- a/query/query-result-set.cc +++ b/query/query-result-set.cc @@ -19,6 +19,8 @@ namespace query { static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); class deserialization_error : public std::runtime_error { public: @@ -50,7 +52,7 @@ private: }; std::ostream& operator<<(std::ostream& out, const result_set_row& row) { - for (auto&& cell : row._cells) { + for (auto&& cell : row.cells()) { auto&& type = static_cast(cell.second).type(); auto&& value = cell.second; out << cell.first << "=\"" << type->to_string(type->decompose(value)) << "\" "; diff --git a/query/query-result-set.hh b/query/query-result-set.hh index 7819ab68b6..301e9a912d 100644 --- a/query/query-result-set.hh +++ b/query/query-result-set.hh @@ -46,7 +46,7 @@ inline bool operator==(const non_null_data_value& x, const non_null_data_value& // including regular column cells, partition keys, as well as static values. class result_set_row { schema_ptr _schema; - const std::unordered_map _cells; + std::unordered_map _cells; public: result_set_row(schema_ptr schema, std::unordered_map&& cells) : _schema{schema} @@ -54,15 +54,16 @@ public: { } result_set_row(result_set_row&&) = default; result_set_row(const result_set_row&) = delete; + result_set_row& operator=(result_set_row&&) = default; result_set_row& operator=(const result_set_row&) = delete; result_set_row copy() const { - return {_schema, std::unordered_map{_cells}}; + return {_schema, std::unordered_map{cells()}}; } // Look up a deserialized row cell value by column name const data_value* get_data_value(const sstring& column_name) const { - auto it = _cells.find(column_name); - if (it == _cells.end()) { + auto it = cells().find(column_name); + if (it == cells().end()) { return nullptr; } return &static_cast(it->second); From e4f0539acfba41fac5dc69d85363212cec971de4 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 12 Feb 2026 11:49:25 +0200 Subject: [PATCH 4/4] query: result_set: change row member to a chunked vector To prevent large memory allocations. Signed-off-by: Benny Halevy --- db/schema_applier.cc | 2 +- query/query-result-set.cc | 4 +++- query/query-result-set.hh | 9 ++++++--- test/boost/multishard_query_test.cc | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/db/schema_applier.cc b/db/schema_applier.cc index 62c0dea672..c90fac6d9d 100644 --- a/db/schema_applier.cc +++ b/db/schema_applier.cc @@ -335,7 +335,7 @@ static std::vector get_primary_key(const std::vector& // Build a map from primary keys to rows. static std::map, const query::result_set_row*> build_row_map(const query::result_set& result) { - const std::vector& rows = result.rows(); + const auto& rows = result.rows(); auto primary_key = get_primary_key_definition(result.schema()); std::map, const query::result_set_row*> ret; for (const auto& row: rows) { diff --git a/query/query-result-set.cc b/query/query-result-set.cc index 5ebe174eec..128cbb80a7 100644 --- a/query/query-result-set.cc +++ b/query/query-result-set.cc @@ -21,6 +21,8 @@ static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); class deserialization_error : public std::runtime_error { public: @@ -33,7 +35,7 @@ public: class result_set_builder { schema_ptr _schema; const partition_slice& _slice; - std::vector _rows; + result_set::rows_type _rows; std::unordered_map _pkey_cells; uint64_t _row_count; public: diff --git a/query/query-result-set.hh b/query/query-result-set.hh index 301e9a912d..8b7f42feb3 100644 --- a/query/query-result-set.hh +++ b/query/query-result-set.hh @@ -104,11 +104,14 @@ public: // deserialized format. To obtain a result set, use the result_set_builder // class as a visitor to query_result::consume() function. class result_set { +public: + using rows_type = utils::chunked_vector; +private: schema_ptr _schema; - std::vector _rows; + rows_type _rows; public: static result_set from_raw_result(schema_ptr, const partition_slice&, const result&); - result_set(schema_ptr s, std::vector&& rows) + result_set(schema_ptr s, rows_type&& rows) : _schema(std::move(s)), _rows{std::move(rows)} { } explicit result_set(const mutation&); @@ -122,7 +125,7 @@ public: } return _rows[idx]; } - const std::vector& rows() const { + const rows_type& rows() const { return _rows; } const schema_ptr& schema() const { diff --git a/test/boost/multishard_query_test.cc b/test/boost/multishard_query_test.cc index 3e6b706721..d944a07997 100644 --- a/test/boost/multishard_query_test.cc +++ b/test/boost/multishard_query_test.cc @@ -450,7 +450,7 @@ private: schema_ptr _s; const query::partition_slice& _slice; uint64_t _page_size = 0; - std::vector _rows; + query::result_set::rows_type _rows; std::optional _last_pkey; std::optional _last_ckey; uint64_t _last_pkey_rows = 0; @@ -816,7 +816,7 @@ SEASTAR_THREAD_TEST_CASE(test_read_reversed) { auto [data_results, _np2] = read_partitions_with_generic_paged_scan(db, s, page_size, std::numeric_limits::max(), stateful, query::full_partition_range, slice); - std::vector expected_rows; + query::result_set::rows_type expected_rows; for (const auto& mut : expected_results) { auto rs = query::result_set(mut); std::ranges::copy(rs.rows() | std::views::transform([](const auto& row) { return row.copy(); }), std::back_inserter(expected_rows));