/* */ /* * Copyright (C) 2015-present ScyllaDB * * Modified by ScyllaDB */ /* * SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0) */ #pragma once #include "bytes.hh" #include "schema_fwd.hh" #include "query-result-reader.hh" #include "cql3/column_specification.hh" #include "cql3/selection/selector.hh" #include "exceptions/exceptions.hh" #include "unimplemented.hh" #include namespace cql3 { class result_set; class result_set_builder; class metadata; class query_options; namespace restrictions { class statement_restrictions; } namespace selection { class raw_selector; class selector_factories; class selectors { public: virtual ~selectors() {} virtual bool requires_thread() const = 0; virtual bool is_aggregate() const = 0; /** * Adds the current row of the specified ResultSetBuilder. * * @param rs the ResultSetBuilder * @throws InvalidRequestException */ virtual void add_input_row(cql_serialization_format sf, result_set_builder& rs) = 0; virtual std::vector get_output_row(cql_serialization_format sf) = 0; virtual void reset() = 0; }; class selection { private: schema_ptr _schema; std::vector _columns; ::shared_ptr _metadata; const bool _collect_timestamps; const bool _collect_TTLs; const bool _contains_static_columns; bool _is_trivial; protected: using trivial = bool_class; selection(schema_ptr schema, std::vector columns, std::vector> metadata_, bool collect_timestamps, bool collect_TTLs, trivial is_trivial = trivial::no); virtual ~selection() {} public: // Overriden by SimpleSelection when appropriate. virtual bool is_wildcard() const { return false; } /** * Checks if this selection contains static columns. * @return true if this selection contains static columns, false otherwise; */ bool contains_static_columns() const { return _contains_static_columns; } /** * Checks if this selection contains only static columns. * @return true if this selection contains only static columns, false otherwise; */ bool contains_only_static_columns() const; /** * Returns the index of the specified column. * * @param def the column definition * @return the index of the specified column */ int32_t index_of(const column_definition& def) const; bool has_column(const column_definition& def) const; ::shared_ptr get_result_metadata() const { return _metadata; } ::shared_ptr get_result_metadata() { return _metadata; } static ::shared_ptr wildcard(schema_ptr schema); static ::shared_ptr for_columns(schema_ptr schema, std::vector columns); virtual uint32_t add_column_for_post_processing(const column_definition& c); query::partition_slice::option_set get_query_options(); private: static bool processes_selection(const std::vector<::shared_ptr>& raw_selectors); static std::vector> collect_metadata(const schema& schema, const std::vector<::shared_ptr>& raw_selectors, const selector_factories& factories); public: static ::shared_ptr from_selectors(data_dictionary::database db, schema_ptr schema, const std::vector<::shared_ptr>& raw_selectors); virtual std::unique_ptr new_selectors() const = 0; /** * Returns a range of CQL3 columns this selection needs. */ auto const& get_columns() const { return _columns; } uint32_t get_column_count() const { return _columns.size(); } virtual bool is_aggregate() const = 0; virtual bool is_count() const {return false;} /** * Checks that selectors are either all aggregates or that none of them is. * * @param selectors the selectors to test. * @param messageTemplate the error message template * @param messageArgs the error message arguments * @throws InvalidRequestException if some of the selectors are aggregate but not all of them */ template static void validate_selectors(const std::vector<::shared_ptr>& selectors, const sstring& msg, Args&&... args) { int32_t aggregates = 0; for (auto&& s : selectors) { if (s->is_aggregate()) { ++aggregates; } } if (aggregates != 0 && aggregates != selectors.size()) { throw exceptions::invalid_request_exception(fmt::format(msg, std::forward(args)...)); } } /** * Returns true if the selection is trivial, i.e. there are no function * selectors (including casts or aggregates). */ bool is_trivial() const { return _is_trivial; } friend class result_set_builder; }; class result_set_builder { private: std::unique_ptr _result_set; std::unique_ptr _selectors; const std::vector _group_by_cell_indices; ///< Indices in \c current of cells holding GROUP BY values. std::vector _last_group; ///< Previous row's group: all of GROUP BY column values. bool _group_began; ///< Whether a group began being formed. public: std::optional> current; private: std::vector _timestamps; std::vector _ttls; const gc_clock::time_point _now; cql_serialization_format _cql_serialization_format; public: template auto with_thread_if_needed(Func&& func) { if (_selectors->requires_thread()) { return async(std::move(func)); } else { return futurize_invoke(std::move(func)); } } class nop_filter { public: inline bool operator()(const selection&, const std::vector&, const std::vector&, const query::result_row_view&, const query::result_row_view*) const { return true; } void reset(const partition_key* = nullptr) { } uint64_t get_rows_dropped() const { return 0; } }; class restrictions_filter { ::shared_ptr _restrictions; const query_options& _options; const bool _skip_pk_restrictions; const bool _skip_ck_restrictions; mutable bool _current_partition_key_does_not_match = false; mutable bool _current_static_row_does_not_match = false; mutable uint64_t _rows_dropped = 0; mutable uint64_t _remaining; schema_ptr _schema; mutable uint64_t _per_partition_limit; mutable uint64_t _per_partition_remaining; mutable uint64_t _rows_fetched_for_last_partition; mutable std::optional _last_pkey; mutable bool _is_first_partition_on_page = true; public: explicit restrictions_filter(::shared_ptr restrictions, const query_options& options, uint64_t remaining, schema_ptr schema, uint64_t per_partition_limit, std::optional last_pkey = {}, uint64_t rows_fetched_for_last_partition = 0); bool operator()(const selection& selection, const std::vector& pk, const std::vector& ck, const query::result_row_view& static_row, const query::result_row_view* row) const; void reset(const partition_key* key = nullptr); uint64_t get_rows_dropped() const { return _rows_dropped; } private: bool do_filter(const selection& selection, const std::vector& pk, const std::vector& ck, const query::result_row_view& static_row, const query::result_row_view* row) const; }; result_set_builder(const selection& s, gc_clock::time_point now, cql_serialization_format sf, std::vector group_by_cell_indices = {}); void add_empty(); void add(bytes_opt value); void add(const column_definition& def, const query::result_atomic_cell_view& c); void add_collection(const column_definition& def, bytes_view c); void new_row(); std::unique_ptr build(); api::timestamp_type timestamp_of(size_t idx); int32_t ttl_of(size_t idx); // Implements ResultVisitor concept from query.hh template class visitor { protected: result_set_builder& _builder; const schema& _schema; const selection& _selection; uint64_t _row_count; std::vector _partition_key; std::vector _clustering_key; Filter _filter; public: visitor(cql3::selection::result_set_builder& builder, const schema& s, const selection& selection, Filter filter = Filter()) : _builder(builder) , _schema(s) , _selection(selection) , _row_count(0) , _filter(filter) {} visitor(visitor&&) = default; void add_value(const column_definition& def, query::result_row_view::iterator_type& i) { if (def.type->is_multi_cell()) { auto cell = i.next_collection_cell(); if (!cell) { _builder.add_empty(); return; } _builder.add_collection(def, cell->linearize()); } else { auto cell = i.next_atomic_cell(); if (!cell) { _builder.add_empty(); return; } _builder.add(def, *cell); } } void accept_new_partition(const partition_key& key, uint64_t row_count) { _partition_key = key.explode(_schema); _row_count = row_count; _filter.reset(&key); } void accept_new_partition(uint64_t row_count) { _row_count = row_count; _filter.reset(); } void accept_new_row(const clustering_key& key, const query::result_row_view& static_row, const query::result_row_view& row) { _clustering_key = key.explode(_schema); accept_new_row(static_row, row); } void accept_new_row(const query::result_row_view& static_row, const query::result_row_view& row) { auto static_row_iterator = static_row.iterator(); auto row_iterator = row.iterator(); if (!_filter(_selection, _partition_key, _clustering_key, static_row, &row)) { return; } _builder.new_row(); for (auto&& def : _selection.get_columns()) { switch (def->kind) { case column_kind::partition_key: _builder.add(_partition_key[def->component_index()]); break; case column_kind::clustering_key: if (_clustering_key.size() > def->component_index()) { _builder.add(_clustering_key[def->component_index()]); } else { _builder.add({}); } break; case column_kind::regular_column: add_value(*def, row_iterator); break; case column_kind::static_column: add_value(*def, static_row_iterator); break; default: assert(0); } } } uint64_t accept_partition_end(const query::result_row_view& static_row) { if (_row_count == 0) { if (!_filter(_selection, _partition_key, _clustering_key, static_row, nullptr)) { return _filter.get_rows_dropped(); } _builder.new_row(); auto static_row_iterator = static_row.iterator(); for (auto&& def : _selection.get_columns()) { if (def->is_partition_key()) { _builder.add(_partition_key[def->component_index()]); } else if (def->is_static()) { add_value(*def, static_row_iterator); } else { _builder.add_empty(); } } } return _filter.get_rows_dropped(); } }; private: bytes_opt get_value(data_type t, query::result_atomic_cell_view c); /// True iff the \c current row ends a previously started group, either according to /// _group_by_cell_indices or aggregation. bool last_group_ended() const; /// If there is a valid row in this->current, process it; if \p more_rows_coming, get ready to /// receive another. void process_current_row(bool more_rows_coming); /// Gets output row from _selectors and resets them. void flush_selectors(); /// Updates _last_group from the \c current row. void update_last_group(); }; } }