static String serializeToString(const AggregateFunctionPtr & function, const IColumn & column, size_t row_num) { String res; WriteBufferFromString buffer(res); function.get()->serialize(static_cast<const ColumnAggregateFunction &>(column).getData()[row_num], buffer); return res; }
static void deserializeFromString(const AggregateFunctionPtr & function, IColumn & column, const String & s) { ColumnAggregateFunction & column_concrete = static_cast<ColumnAggregateFunction &>(column); size_t size_of_state = function->sizeOfData(); AggregateDataPtr place = column_concrete.createOrGetArena().alloc(size_of_state); function->create(place); try { ReadBufferFromString istr(s); function->deserialize(place, istr); } catch (...) { function->destroy(place); throw; } column_concrete.getData().push_back(place); }
DataTypePtr FunctionArrayReduce::getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const { /// The first argument is a constant string with the name of the aggregate function /// (possibly with parameters in parentheses, for example: "quantile(0.99)"). if (arguments.size() < 2) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) + ", should be at least 2.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); const ColumnConst * aggregate_function_name_column = checkAndGetColumnConst<ColumnString>(arguments[0].column.get()); if (!aggregate_function_name_column) throw Exception("First argument for function " + getName() + " must be constant string: name of aggregate function.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); DataTypes argument_types(arguments.size() - 1); for (size_t i = 1, size = arguments.size(); i < size; ++i) { const DataTypeArray * arg = checkAndGetDataType<DataTypeArray>(arguments[i].type.get()); if (!arg) throw Exception("Argument " + toString(i) + " for function " + getName() + " must be an array but it has type " + arguments[i].type->getName() + ".", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); argument_types[i - 1] = arg->getNestedType(); } if (!aggregate_function) { String aggregate_function_name_with_params = aggregate_function_name_column->getValue<String>(); if (aggregate_function_name_with_params.empty()) throw Exception("First argument for function " + getName() + " (name of aggregate function) cannot be empty.", ErrorCodes::BAD_ARGUMENTS); String aggregate_function_name; Array params_row; getAggregateFunctionNameAndParametersArray(aggregate_function_name_with_params, aggregate_function_name, params_row, "function " + getName()); aggregate_function = AggregateFunctionFactory::instance().get(aggregate_function_name, argument_types, params_row); } return aggregate_function->getReturnType(); }
AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const DataTypes & argument_types, int recursion_level) const { auto it = aggregate_functions.find(name); if (it != aggregate_functions.end()) { const auto & desc = it->second; const auto & creator = desc.creator; return creator(name, argument_types); } else if ((recursion_level == 0) && endsWith<SuffixState>(name)) { /// Для агрегатных функций вида aggState, где agg - имя другой агрегатной функции. AggregateFunctionPtr nested = get(trimRight<SuffixState>(name), argument_types, recursion_level + 1); return createAggregateFunctionState(nested); } else if ((recursion_level <= 1) && endsWith<SuffixMerge>(name)) { /// Для агрегатных функций вида aggMerge, где agg - имя другой агрегатной функции. if (argument_types.size() != 1) throw Exception("Incorrect number of arguments for aggregate function " + name, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); const DataTypeAggregateFunction * function = typeid_cast<const DataTypeAggregateFunction *>(&*argument_types[0]); if (!function) throw Exception("Illegal type " + argument_types[0]->getName() + " of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); AggregateFunctionPtr nested = get(trimRight<SuffixMerge>(name), function->getArgumentsDataTypes(), recursion_level + 1); if (nested->getName() != function->getFunctionName()) throw Exception("Illegal type " + argument_types[0]->getName() + " of argument for aggregate function " + name, ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return createAggregateFunctionMerge(nested); } else if ((recursion_level <= 2) && endsWith<SuffixIf>(name)) { if (argument_types.empty()) throw Exception{ "Incorrect number of arguments for aggregate function " + name, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH }; /// Для агрегатных функций вида aggIf, где agg - имя другой агрегатной функции. DataTypes nested_dt = argument_types; nested_dt.pop_back(); AggregateFunctionPtr nested = get(trimRight<SuffixIf>(name), nested_dt, recursion_level + 1); return createAggregateFunctionIf(nested); } else if ((recursion_level <= 3) && endsWith<SuffixArray>(name)) { /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. size_t num_agruments = argument_types.size(); DataTypes nested_arguments; for (size_t i = 0; i < num_agruments; ++i) { if (const DataTypeArray * array = typeid_cast<const DataTypeArray *>(&*argument_types[i])) nested_arguments.push_back(array->getNestedType()); else throw Exception("Illegal type " + argument_types[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + name + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } /// + 3, чтобы ни один другой модификатор не мог идти перед Array AggregateFunctionPtr nested = get(trimRight<SuffixArray>(name), nested_arguments, recursion_level + 3); return createAggregateFunctionArray(nested); } else throw Exception("Unknown aggregate function " + name, ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); }
void FunctionArrayReduce::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) { IAggregateFunction & agg_func = *aggregate_function.get(); AlignedBuffer place_holder(agg_func.sizeOfData(), agg_func.alignOfData()); AggregateDataPtr place = place_holder.data(); std::unique_ptr<Arena> arena = agg_func.allocatesMemoryInArena() ? std::make_unique<Arena>() : nullptr; size_t rows = input_rows_count; /// Aggregate functions do not support constant columns. Therefore, we materialize them. std::vector<ColumnPtr> materialized_columns; const size_t num_arguments_columns = arguments.size() - 1; std::vector<const IColumn *> aggregate_arguments_vec(num_arguments_columns); const ColumnArray::Offsets * offsets = nullptr; for (size_t i = 0; i < num_arguments_columns; ++i) { const IColumn * col = block.getByPosition(arguments[i + 1]).column.get(); const ColumnArray::Offsets * offsets_i = nullptr; if (const ColumnArray * arr = checkAndGetColumn<ColumnArray>(col)) { aggregate_arguments_vec[i] = &arr->getData(); offsets_i = &arr->getOffsets(); } else if (const ColumnConst * const_arr = checkAndGetColumnConst<ColumnArray>(col)) { materialized_columns.emplace_back(const_arr->convertToFullColumn()); const auto & arr = typeid_cast<const ColumnArray &>(*materialized_columns.back().get()); aggregate_arguments_vec[i] = &arr.getData(); offsets_i = &arr.getOffsets(); } else throw Exception("Illegal column " + col->getName() + " as argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); if (i == 0) offsets = offsets_i; else if (*offsets_i != *offsets) throw Exception("Lengths of all arrays passed to " + getName() + " must be equal.", ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH); } const IColumn ** aggregate_arguments = aggregate_arguments_vec.data(); MutableColumnPtr result_holder = block.getByPosition(result).type->createColumn(); IColumn & res_col = *result_holder; /// AggregateFunction's states should be inserted into column using specific way auto res_col_aggregate_function = typeid_cast<ColumnAggregateFunction *>(&res_col); if (!res_col_aggregate_function && agg_func.isState()) throw Exception("State function " + agg_func.getName() + " inserts results into non-state column " + block.getByPosition(result).type->getName(), ErrorCodes::ILLEGAL_COLUMN); ColumnArray::Offset current_offset = 0; for (size_t i = 0; i < rows; ++i) { agg_func.create(place); ColumnArray::Offset next_offset = (*offsets)[i]; try { for (size_t j = current_offset; j < next_offset; ++j) agg_func.add(place, aggregate_arguments, j, arena.get()); if (!res_col_aggregate_function) agg_func.insertResultInto(place, res_col); else res_col_aggregate_function->insertFrom(place); } catch (...) { agg_func.destroy(place); throw; } agg_func.destroy(place); current_offset = next_offset; } block.getByPosition(result).column = std::move(result_holder); }