#pragma once #include #include #include #include namespace DB { /** Соединяет несколько сортированных потоков в один. * При этом, для каждой группы идущих подряд одинаковых значений первичного ключа (столбцов, по которым сортируются данные), * схлопывает их в одну строку, суммируя все числовые столбцы кроме первичного ключа. * Если во всех числовых столбцах кроме первичного ключа получился ноль, то удаляет строчку. */ class SummingSortedBlockInputStream : public MergingSortedBlockInputStream { public: SummingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, size_t max_block_size_) : MergingSortedBlockInputStream(inputs_, description_, max_block_size_), log(&Logger::get("SummingSortedBlockInputStream")), current_row_is_zero(false), output_is_non_empty(false) { } String getName() const { return "SummingSortedBlockInputStream"; } String getID() const { std::stringstream res; res << "SummingSorted(inputs"; for (size_t i = 0; i < children.size(); ++i) res << ", " << children[i]->getID(); res << ", description"; for (size_t i = 0; i < description.size(); ++i) res << ", " << description[i].getID(); res << ")"; return res.str(); } protected: /// Может возвращаться на 1 больше записей, чем max_block_size. Block readImpl(); private: Logger * log; /// Столбцы с какими номерами надо суммировать. ColumnNumbers column_numbers_to_sum; Row current_key; /// Текущий первичный ключ. Row next_key; /// Первичный ключ следующей строки. Row current_row; bool current_row_is_zero; /// Текущая строчка просуммировалась в ноль, и её следует удалить. bool output_is_non_empty; /// Отдали ли мы наружу хоть одну строку. /** Делаем поддержку двух разных курсоров - с Collation и без. * Шаблоны используем вместо полиморфных SortCursor'ов и вызовов виртуальных функций. */ template void merge(Block & merged_block, ColumnPlainPtrs & merged_columns, std::priority_queue & queue); /// Вставить в результат просуммированную строку для текущей группы. void insertCurrentRow(ColumnPlainPtrs & merged_columns); /** Реализует операцию +=. * Возвращает false, если результат получился нулевым. */ class FieldVisitorSum : public StaticVisitor { private: const Field & rhs; public: FieldVisitorSum(const Field & rhs_) : rhs(rhs_) {} bool operator() (UInt64 & x) const { x += get(rhs); return x != 0; } bool operator() (Int64 & x) const { x += get(rhs); return x != 0; } bool operator() (Float64 & x) const { x += get(rhs); return x != 0; } bool operator() (Null & x) const { throw Exception("Cannot sum Nulls", ErrorCodes::LOGICAL_ERROR); } bool operator() (String & x) const { throw Exception("Cannot sum Strings", ErrorCodes::LOGICAL_ERROR); } bool operator() (Array & x) const { throw Exception("Cannot sum Arrays", ErrorCodes::LOGICAL_ERROR); } }; /** Прибавить строчку под курсором к row. * Возвращает false, если результат получился нулевым. */ template bool addRow(Row & row, TSortCursor & cursor) { bool res = false; /// Есть ли хотя бы одно ненулевое число. for (size_t i = 0, size = column_numbers_to_sum.size(); i < size; ++i) { size_t j = column_numbers_to_sum[i]; if (apply_visitor(FieldVisitorSum((*cursor->all_columns[j])[cursor->pos]), row[j])) res = true; } return res; } }; }