diff --git a/dbms/src/Functions/IFunction.h b/dbms/src/Functions/IFunction.h index e5c24b98cec..ce15891f30a 100644 --- a/dbms/src/Functions/IFunction.h +++ b/dbms/src/Functions/IFunction.h @@ -159,7 +159,12 @@ public: */ virtual bool isSuitableForConstantFolding() const { return true; } - virtual bool alwaysReturnsConstant() const { return false; } + /** Some functions like ignore(...) or toTypeName(...) always return constant result which doesn't depend on arguments. + * In this case we can calculate result and assume that it's constant in stream header. + * There is no need to implement function if it has zero arguments. + * Must return ColumnConst with single row or nullptr. + */ + virtual ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & /*block*/, const ColumnNumbers & /*arguments*/) const { return nullptr; } /** Function is called "injective" if it returns different result for different values of arguments. * Example: hex, negate, tuple... @@ -458,7 +463,10 @@ public: } bool isSuitableForConstantFolding() const override { return function->isSuitableForConstantFolding(); } - bool alwaysReturnsConstant() const override { return function->alwaysReturnsConstant(); } + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments_) const override + { + return function->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments_); + } bool isInjective(const Block & sample_block) override { return function->isInjective(sample_block); } diff --git a/dbms/src/Functions/defaultValueOfArgumentType.cpp b/dbms/src/Functions/defaultValueOfArgumentType.cpp index 452f25b51d8..db1a1533163 100644 --- a/dbms/src/Functions/defaultValueOfArgumentType.cpp +++ b/dbms/src/Functions/defaultValueOfArgumentType.cpp @@ -37,6 +37,12 @@ public: const IDataType & type = *block.getByPosition(arguments[0]).type; block.getByPosition(result).column = type.createColumnConst(input_rows_count, type.getDefault()); } + + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const override + { + const IDataType & type = *block.getByPosition(arguments[0]).type; + return type.createColumnConst(1, type.getDefault()); + } }; diff --git a/dbms/src/Functions/getSizeOfEnumType.cpp b/dbms/src/Functions/getSizeOfEnumType.cpp index 11a22ecddfb..9b598ccca3c 100644 --- a/dbms/src/Functions/getSizeOfEnumType.cpp +++ b/dbms/src/Functions/getSizeOfEnumType.cpp @@ -49,11 +49,16 @@ public: } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + block.getByPosition(result).column = getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments)->cloneResized(input_rows_count); + } + + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const override { if (auto type8 = checkAndGetDataType(block.getByPosition(arguments[0]).type.get())) - block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, type8->getValues().size()); + return DataTypeUInt8().createColumnConst(1, type8->getValues().size()); else if (auto type16 = checkAndGetDataType(block.getByPosition(arguments[0]).type.get())) - block.getByPosition(result).column = DataTypeUInt16().createColumnConst(input_rows_count, type16->getValues().size()); + return DataTypeUInt16().createColumnConst(1, type16->getValues().size()); else throw Exception("The argument for function " + getName() + " must be Enum", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } diff --git a/dbms/src/Functions/ignore.cpp b/dbms/src/Functions/ignore.cpp index c3ed34ac994..592de576659 100644 --- a/dbms/src/Functions/ignore.cpp +++ b/dbms/src/Functions/ignore.cpp @@ -43,7 +43,10 @@ public: block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, 0u); } - bool alwaysReturnsConstant() const override { return true; } + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block &, const ColumnNumbers &) const override + { + return DataTypeUInt8().createColumnConst(1, 0u); + } }; diff --git a/dbms/src/Functions/toColumnTypeName.cpp b/dbms/src/Functions/toColumnTypeName.cpp index 35d332d082f..023a9350547 100644 --- a/dbms/src/Functions/toColumnTypeName.cpp +++ b/dbms/src/Functions/toColumnTypeName.cpp @@ -38,6 +38,11 @@ public: block.getByPosition(result).column = DataTypeString().createColumnConst(input_rows_count, block.getByPosition(arguments[0]).column->getName()); } + + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const override + { + return DataTypeString().createColumnConst(1, block.getByPosition(arguments[0]).type->createColumn()->getName()); + } }; diff --git a/dbms/src/Functions/toTypeName.cpp b/dbms/src/Functions/toTypeName.cpp index a631c7f4044..22d7d7eb023 100644 --- a/dbms/src/Functions/toTypeName.cpp +++ b/dbms/src/Functions/toTypeName.cpp @@ -9,33 +9,16 @@ namespace DB /** toTypeName(x) - get the type name * Returns name of IDataType instance (name of data type). */ -class FunctionToTypeName : public IFunction +class PreparedFunctionToTypeName : public PreparedFunctionImpl { public: static constexpr auto name = "toTypeName"; - static FunctionPtr create(const Context &) - { - return std::make_shared(); - } - - String getName() const override - { - return name; - } + String getName() const override { return name; } +protected: bool useDefaultImplementationForNulls() const override { return false; } bool useDefaultImplementationForLowCardinalityColumns() const override { return false; } - size_t getNumberOfArguments() const override - { - return 1; - } - - DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override - { - return std::make_shared(); - } - /// Execute the function on the block. void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override { @@ -45,9 +28,55 @@ public: }; +class BaseFunctionToTypeName : public IFunctionBase +{ +public: + BaseFunctionToTypeName(DataTypes argument_types_, DataTypePtr return_type_) + : argument_types(std::move(argument_types_)), return_type(std::move(return_type_)) {} + + static constexpr auto name = "toTypeName"; + String getName() const override { return name; } + + const DataTypes & getArgumentTypes() const override { return argument_types; } + const DataTypePtr & getReturnType() const override { return return_type; } + + PreparedFunctionPtr prepare(const Block &, const ColumnNumbers &, size_t) const override + { + return std::make_shared(); + } + + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block &, const ColumnNumbers &) const override + { + return DataTypeString().createColumnConst(1, argument_types.at(0)->getName()); + } + +private: + DataTypes argument_types; + DataTypePtr return_type; +}; + + +class FunctionToTypeNameBuilder : public FunctionBuilderImpl +{ + static constexpr auto name = "toTypeName"; + String getName() const override { return name; } + static FunctionBuilderPtr create(const Context &) { return std::make_shared(); } + + size_t getNumberOfArguments() const override { return 1; } + +protected: + DataTypePtr getReturnTypeImpl(const DataTypes &) const override { return std::make_shared(); } + + FunctionBasePtr buildImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & return_type) const override + { + return std::make_shared(arguments, return_type); + } +}; + + void registerFunctionToTypeName(FunctionFactory & factory) { - factory.registerFunction(); + factory.registerFunction(); } } diff --git a/dbms/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp b/dbms/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp index 15cefe3c27c..905827205b4 100644 --- a/dbms/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp +++ b/dbms/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp @@ -68,7 +68,10 @@ BlockInputStreamPtr createLocalStream(const ASTPtr & query_ast, const Context & * If you do not do this, different types (Const and non-Const) columns will be produced in different threads, * And this is not allowed, since all code is based on the assumption that in the block stream all types are the same. */ - /// Already not. + + /* Now we don't need to materialize constants, because RemoteBlockInputStream will ignore constant and take it from header. + * So, streams from different threads will always have the same header. + */ /// return std::make_shared(stream); return stream; diff --git a/dbms/src/Interpreters/ExpressionActions.cpp b/dbms/src/Interpreters/ExpressionActions.cpp index d1808442ab2..75545abc210 100644 --- a/dbms/src/Interpreters/ExpressionActions.cpp +++ b/dbms/src/Interpreters/ExpressionActions.cpp @@ -246,10 +246,13 @@ void ExpressionAction::prepare(Block & sample_block, const Settings & settings, } auto & res = sample_block.getByPosition(result_position); - if (!res.column && function_base->alwaysReturnsConstant() && function_base->isSuitableForConstantFolding()) + if (!res.column && function_base->isSuitableForConstantFolding()) { - res.column = result_type->createColumnConstWithDefaultValue(1); - names_not_for_constant_folding.insert(result_name); + if (auto col = function_base->getResultIfAlwaysReturnsConstantAndHasArguments(sample_block, arguments)) + { + res.column = std::move(col); + names_not_for_constant_folding.insert(result_name); + } } break;