#include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_INDEX; } namespace { /** Extract element of tuple by constant index or name. The operation is essentially free. * Also the function looks through Arrays: you can get Array of tuple elements from Array of Tuples. */ class FunctionNamedTupleItems : public IFunction { public: static constexpr auto name = "namedTupleItems"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } size_t getNumberOfArguments() const override { return 1; } bool useDefaultImplementationForConstants() const override { return true; } DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { // get the type of all the fields in the tuple const IDataType * col = arguments[0].type.get(); const DataTypeTuple * tuple = checkAndGetDataType(col); if (!tuple) throw Exception("First argument for function " + getName() + " must " "be a tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); const auto& elementTypes = tuple->getElements(); if (elementTypes.empty()) throw Exception("The argument tuple for function " + getName() + " must " "not be empty.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); const auto& firstElementType = elementTypes[0]; auto it = std::find_if( elementTypes.begin() + 1, elementTypes.end(), [&](const auto &other) { return !firstElementType->equals(*other); }); if (it != elementTypes.end()) { throw Exception("The argument tuple for function " + getName() + " must " "contain just one type", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } DataTypePtr tupleNameType = std::make_shared(); DataTypes itemDataTypes ={tupleNameType, firstElementType}; auto itemDataType = std::make_shared(itemDataTypes); return std::make_shared(itemDataType); } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override { const IColumn *tuple_col = arguments[0].column.get(); const DataTypeTuple * tuple = checkAndGetDataType(arguments[0].type.get()); auto *tuple_col_concrete = assert_cast(tuple_col); MutableColumnPtr keys = ColumnString::create(); MutableColumnPtr values = tuple_col_concrete->getColumn(0).cloneEmpty(); auto offsets = ColumnVector::create(); for (size_t row = 0; row < tuple_col_concrete->size(); ++row) { for (size_t col = 0; col < tuple_col_concrete->tupleSize(); ++col) { const std::string& key = tuple->getElementNames()[col]; const IColumn& valueColumn = tuple_col_concrete->getColumn(col); values->insertFrom(valueColumn, row); keys->insertData(key.data(), key.size()); } offsets->insertValue(tuple_col_concrete->tupleSize() * (row + 1)); } std::vector tupleColumns = { std::move(keys), std::move(values) }; auto tupleColumn = ColumnTuple::create(std::move(tupleColumns)); return ColumnArray::create(std::move(tupleColumn), std::move(offsets)); } }; } void registerFunctionNamedTupleItems(FunctionFactory & factory) { factory.registerFunction(); } }