#pragma once #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int LOGICAL_ERROR; extern const int ILLEGAL_TYPE_OF_ARGUMENT; } namespace Conditional { namespace { /// This class provides type-independent access to the values of a numeric branch /// (then, else) column. Returned values have the type TResult. template class NumericSource { public: virtual TResult get(size_t i) const = 0; virtual ~NumericSource() = default; }; template using NumericSourcePtr = std::unique_ptr >; template using NumericSources = std::vector >; /// Column type-specific implementation of NumericSource. template class NumericSourceImpl final : public NumericSource { public: NumericSourceImpl(const Block & block, const ColumnNumbers & args, const Branch & br) : data_array{initDataArray(block, args, br)} { size_t index = br.index; bool is_const = br.is_const; if (is_const) { const ColumnPtr & col = block.getByPosition(args[index]).column; const auto * const_col = typeid_cast *>(&*col); if (const_col == nullptr) throw Exception{"Internal error", ErrorCodes::LOGICAL_ERROR}; TType data = const_col->getData(); accessor = [data](size_t i) { return static_cast(data); }; } else accessor = [&](size_t i) { return static_cast(data_array[i]); }; } NumericSourceImpl(const NumericSourceImpl &) = delete; NumericSourceImpl & operator=(const NumericSourceImpl &) = delete; NumericSourceImpl(NumericSourceImpl &&) = default; NumericSourceImpl & operator=(NumericSourceImpl &&) = default; TResult get(size_t i) const override { return accessor(i); }; private: static const PaddedPODArray & initDataArray(const Block & block, const ColumnNumbers & args, const Branch & br) { bool is_const = br.is_const; if (is_const) return null_array; else { size_t index = br.index; const ColumnPtr & col = block.getByPosition(args[index]).column; const auto * vec_col = typeid_cast *>(&*col); if (vec_col == nullptr) throw Exception{"Internal error", ErrorCodes::LOGICAL_ERROR}; return vec_col->getData(); } } private: static const PaddedPODArray null_array; const PaddedPODArray & data_array; using Accessor = std::function; Accessor accessor; }; template const PaddedPODArray NumericSourceImpl::null_array{}; /// Create a numeric column accessor if TType is the type registered /// in the specified branch info. template class NumericSourceCreator final { public: static bool execute(NumericSourcePtr & source, const Block & block, const ColumnNumbers & args, const Branch & br) { auto type_name = br.type->getName(); if (TypeName::get() == type_name) { source = std::make_unique >(block, args, br); return true; } else return false; } }; } /// Processing of multiIf in the case of scalar numeric types. template class NumericEvaluator final { public: static void perform(const Branches & branches, Block & block, const ColumnNumbers & args, size_t result) { const CondSources conds = createConds(block, args); const NumericSources sources = createNumericSources(block, args, branches); size_t row_count = conds[0].getSize(); PaddedPODArray & res = createSink(block, result, row_count); for (size_t cur_row = 0; cur_row < row_count; ++cur_row) { bool has_triggered_cond = false; size_t cur_source = 0; for (const auto & cond : conds) { if (cond.get(cur_row)) { res[cur_row] = sources[cur_source]->get(cur_row); has_triggered_cond = true; break; } ++cur_source; } if (!has_triggered_cond) res[cur_row] = sources.back()->get(cur_row); } } private: template using ConcreteNumericSourceCreator = NumericSourceCreator; private: /// Create the result column. static PaddedPODArray & createSink(Block & block, size_t result, size_t size) { ColumnVector * col_res = new ColumnVector; block.getByPosition(result).column = col_res; typename ColumnVector::Container_t & vec_res = col_res->getData(); vec_res.resize(size); return vec_res; } /// Create accessors for condition values. static CondSources createConds(const Block & block, const ColumnNumbers & args) { CondSources conds; conds.reserve(getCondCount(args)); for (size_t i = firstCond(); i < elseArg(args); i = nextCond(i)) conds.emplace_back(block, args, i); return conds; } /// Create accessors for branch values. static NumericSources createNumericSources(const Block & block, const ColumnNumbers & args, const Branches & branches) { NumericSources sources; sources.reserve(branches.size()); for (const auto & br : branches) { NumericSourcePtr source; if (!NumericTypeDispatcher::apply(source, block, args, br)) throw Exception{"Illegal type of argument " + toString(br.index) + " of function multiIf", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; sources.push_back(std::move(source)); } return sources; } }; /// Processing of multiIf in the case of an invalid return type. template <> class NumericEvaluator { public: static void perform(const Branches & branches, Block & block, const ColumnNumbers & args, size_t result) { throw Exception{"Internal logic error", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; } }; } }