/* * Copyright (C) 2015-present ScyllaDB */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 */ #include "query-result-set.hh" #include "query-result-reader.hh" #include "partition_slice_builder.hh" #include "mutation/mutation.hh" #include "types/map.hh" #include "mutation_query.hh" #include namespace query { class deserialization_error : public std::runtime_error { public: using runtime_error::runtime_error; }; // 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; const partition_slice& _slice; std::vector _rows; std::unordered_map _pkey_cells; uint64_t _row_count; public: // Keep slice live as long as the builder is used. result_set_builder(schema_ptr schema, const partition_slice& slice); result_set build(); void accept_new_partition(const partition_key& key, uint64_t row_count); void accept_new_partition(uint64_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 = static_cast(cell.second).type(); auto&& value = cell.second; out << cell.first << "=\"" << type->to_string(type->decompose(value)) << "\" "; } return out; } std::ostream& operator<<(std::ostream& out, const result_set& rs) { for (auto&& row : rs._rows) { out << row << std::endl; } return out; } static logging::logger query_result_log("query_result_log"); non_null_data_value::non_null_data_value(data_value&& v) : _v(std::move(v)) { if (_v.is_null()) { on_internal_error(query_result_log, "Trying to add a null data_value to a result_set_row"); } } result_set_builder::result_set_builder(schema_ptr schema, const partition_slice& slice) : _schema{schema}, _slice(slice) { } result_set result_set_builder::build() { return { _schema, std::move(_rows) }; } void result_set_builder::accept_new_partition(const partition_key& key, uint64_t row_count) { _pkey_cells = deserialize(key); accept_new_partition(row_count); } void result_set_builder::accept_new_partition(uint64_t row_count) { _row_count = row_count; } void result_set_builder::accept_new_row(const clustering_key& key, const result_row_view& static_row, const result_row_view& row) { auto ckey_cells = deserialize(key); auto static_cells = deserialize(static_row, true); auto regular_cells = deserialize(row, false); std::unordered_map cells; cells.insert(_pkey_cells.begin(), _pkey_cells.end()); cells.insert(ckey_cells.begin(), ckey_cells.end()); cells.insert(static_cells.begin(), static_cells.end()); cells.insert(regular_cells.begin(), regular_cells.end()); _rows.emplace_back(_schema, std::move(cells)); } void result_set_builder::accept_new_row(const query::result_row_view &static_row, const query::result_row_view &row) { auto static_cells = deserialize(static_row, true); auto regular_cells = deserialize(row, false); std::unordered_map cells; cells.insert(_pkey_cells.begin(), _pkey_cells.end()); cells.insert(static_cells.begin(), static_cells.end()); cells.insert(regular_cells.begin(), regular_cells.end()); _rows.emplace_back(_schema, std::move(cells)); } void result_set_builder::accept_partition_end(const result_row_view& static_row) { if (_row_count == 0) { auto static_cells = deserialize(static_row, true); std::unordered_map cells; cells.insert(_pkey_cells.begin(), _pkey_cells.end()); cells.insert(static_cells.begin(), static_cells.end()); _rows.emplace_back(_schema, std::move(cells)); } _pkey_cells.clear(); } std::unordered_map result_set_builder::deserialize(const partition_key& key) { std::unordered_map cells; auto i = key.begin(*_schema); for (auto&& col : _schema->partition_key_columns()) { cells.emplace(col.name_as_text(), col.type->deserialize_value(*i)); ++i; } return cells; } std::unordered_map result_set_builder::deserialize(const clustering_key& key) { std::unordered_map cells; auto i = key.begin(*_schema); for (auto&& col : _schema->clustering_key_columns()) { if (i == key.end(*_schema)) { break; } cells.emplace(col.name_as_text(), col.type->deserialize_value(*i)); ++i; } return cells; } std::unordered_map result_set_builder::deserialize(const result_row_view& row, bool is_static) { std::unordered_map cells; auto i = row.iterator(); auto column_ids = is_static ? _slice.static_columns : _slice.regular_columns; auto columns = column_ids | std::views::transform([this, is_static] (column_id id) -> const column_definition& { if (is_static) { return _schema->static_column_at(id); } else { return _schema->regular_column_at(id); } }); size_t index = 0; for (auto &&col : columns) { try { if (col.is_atomic()) { auto cell = i.next_atomic_cell(); if (cell) { cells.emplace(col.name_as_text(), col.type->deserialize_value(cell->value())); } } else { auto cell = i.next_collection_cell(); if (cell) { if (col.type->is_collection()) { auto ctype = static_pointer_cast(col.type); if (_slice.options.contains()) { ctype = map_type_impl::get_instance(ctype->name_comparator(), ctype->value_comparator(), true); } cells.emplace(col.name_as_text(), ctype->deserialize_value(*cell)); } else { cells.emplace(col.name_as_text(), col.type->deserialize_value(*cell)); } } } index++; } catch (...) { throw deserialization_error(fmt::format(FMT_STRING("failed on column {}.{}#{} (version: {}, id: {}, index: {}, type: {}): {}"), _schema->ks_name(), _schema->cf_name(), col.name_as_text(), _schema->version(), col.id, index, col.type->name(), std::current_exception())); } } return cells; } result_set result_set::from_raw_result(schema_ptr s, const partition_slice& slice, const result& r) { result_set_builder builder{std::move(s), slice}; result_view::consume(r, slice, builder); return builder.build(); } result_set::result_set(const mutation& m) : result_set([&m] { auto slice = partition_slice_builder(*m.schema()).build(); auto qr = query_mutation(mutation(m), slice); return result_set::from_raw_result(m.schema(), slice, qr); }()) { } }