diff --git a/src/Functions/FunctionsJSON.cpp b/src/Functions/FunctionsJSON.cpp index b220a59f8eb..47f485e9d6c 100644 --- a/src/Functions/FunctionsJSON.cpp +++ b/src/Functions/FunctionsJSON.cpp @@ -58,21 +58,21 @@ size_t FunctionJSONHelpers::calculateMaxSize(const ColumnString::Offsets & offse void registerFunctionsJSON(FunctionFactory & factory) { - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); - factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction>(); } } diff --git a/src/Functions/FunctionsJSON.h b/src/Functions/FunctionsJSON.h index 68a29615fa8..4e0b8f291ca 100644 --- a/src/Functions/FunctionsJSON.h +++ b/src/Functions/FunctionsJSON.h @@ -26,9 +26,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -276,37 +278,139 @@ private: template typename Impl> -class FunctionJSON : public IFunction, WithContext +class ExecutableFunctionJSON : public IExecutableFunction, WithContext { + public: - static FunctionPtr create(ContextPtr context_) { return std::make_shared(context_); } - FunctionJSON(ContextPtr context_) : WithContext(context_) {} - - static constexpr auto name = Name::name; - String getName() const override { return Name::name; } - bool isVariadic() const override { return true; } - size_t getNumberOfArguments() const override { return 0; } - bool useDefaultImplementationForConstants() const override { return true; } - bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } - - DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + explicit ExecutableFunctionJSON(const NullPresence & null_presence_, bool allow_simdjson_) + : null_presence(null_presence_), allow_simdjson(allow_simdjson_) { - return Impl::getReturnType(Name::name, arguments); } + String getName() const override { return Name::name; } + bool useDefaultImplementationForNulls() const override { return false; } + bool useDefaultImplementationForConstants() const override { return true; } + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override { + if (null_presence.has_null_constant) + return result_type->createColumnConstWithDefaultValue(input_rows_count); + + ColumnsWithTypeAndName temporary_columns; + if (null_presence.has_nullable) + temporary_columns = createBlockWithNestedColumns(arguments); + else + temporary_columns = arguments; + + ColumnPtr temporary_result; + /// Choose JSONParser. #if USE_SIMDJSON - if (getContext()->getSettingsRef().allow_simdjson) - return FunctionJSONHelpers::Executor::run(arguments, result_type, input_rows_count); + if (allow_simdjson) + temporary_result = FunctionJSONHelpers::Executor::run( + temporary_columns, result_type, input_rows_count); + else #endif - + { #if USE_RAPIDJSON - return FunctionJSONHelpers::Executor::run(arguments, result_type, input_rows_count); + temporary_result = FunctionJSONHelpers::Executor::run( + temporary_columns, result_type, input_rows_count); #else - return FunctionJSONHelpers::Executor::run(arguments, result_type, input_rows_count); + temporary_result + = FunctionJSONHelpers::Executor::run(temporary_columns, result_type, input_rows_count); #endif + } + + if (null_presence.has_nullable) + return wrapInNullable(temporary_result, arguments, result_type, input_rows_count); + return temporary_result; + } + +private: + NullPresence null_presence; + bool allow_simdjson; +}; + + +template typename Impl> +class FunctionBaseFunctionJSON : public IFunctionBase +{ +public: + explicit FunctionBaseFunctionJSON( + const NullPresence & null_presence_, bool allow_simdjson_, DataTypes argument_types_, DataTypePtr return_type_) + : null_presence(null_presence_) + , allow_simdjson(allow_simdjson_) + , argument_types(std::move(argument_types_)) + , return_type(std::move(return_type_)) + { + } + + String getName() const override { return Name::name; } + + const DataTypes & getArgumentTypes() const override + { + return argument_types; + } + + const DataTypePtr & getResultType() const override + { + return return_type; + } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } + + ExecutableFunctionPtr prepare(const ColumnsWithTypeAndName &) const override + { + return std::make_unique>(null_presence, allow_simdjson); + } + +private: + NullPresence null_presence; + bool allow_simdjson; + DataTypes argument_types; + DataTypePtr return_type; +}; + + +template typename Impl> +class JSONOverloadResolver : public IFunctionOverloadResolver, WithContext +{ +public: + static constexpr auto name = Name::name; + + String getName() const override { return name; } + + static FunctionOverloadResolverPtr create(ContextPtr context_) + { + return std::make_unique(context_); + } + + explicit JSONOverloadResolver(ContextPtr context_) : WithContext(context_) {} + + bool isVariadic() const override { return true; } + size_t getNumberOfArguments() const override { return 0; } + + // Both NULL and JSON NULL should generate NULL value. + // If any argument is NULL, return NULL. + bool useDefaultImplementationForNulls() const override { return false; } + + FunctionBasePtr build(const ColumnsWithTypeAndName & arguments) const override + { + NullPresence null_presence = getNullPresense(arguments); + DataTypePtr return_type; + if (null_presence.has_null_constant) + return_type = makeNullable(std::make_shared()); + else if (null_presence.has_nullable) + return_type = makeNullable(Impl::getReturnType(Name::name, createBlockWithNestedColumns(arguments))); + else + return_type = Impl::getReturnType(Name::name, arguments); + + DataTypes argument_types; + argument_types.reserve(arguments.size()); + for (const auto & argument : arguments) + argument_types.emplace_back(argument.type); + return std::make_unique>( + null_presence, getContext()->getSettingsRef().allow_simdjson, argument_types, return_type); } }; diff --git a/src/Functions/IFunction.h b/src/Functions/IFunction.h index db8a449f2b1..db09351a92e 100644 --- a/src/Functions/IFunction.h +++ b/src/Functions/IFunction.h @@ -278,9 +278,7 @@ class IFunctionOverloadResolver public: virtual ~IFunctionOverloadResolver() = default; - FunctionBasePtr build(const ColumnsWithTypeAndName & arguments) const; - - DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const; + virtual FunctionBasePtr build(const ColumnsWithTypeAndName & arguments) const; void getLambdaArgumentTypes(DataTypes & arguments) const; @@ -322,7 +320,10 @@ public: protected: - virtual FunctionBasePtr buildImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const = 0; + virtual FunctionBasePtr buildImpl(const ColumnsWithTypeAndName & /* arguments */, const DataTypePtr & /* result_type */) const + { + throw Exception("buildImpl is not implemented for " + getName(), ErrorCodes::NOT_IMPLEMENTED); + } virtual DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const { @@ -360,6 +361,8 @@ protected: private: + DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const; + DataTypePtr getReturnTypeWithoutLowCardinality(const ColumnsWithTypeAndName & arguments) const; }; diff --git a/tests/queries/0_stateless/02013_json_function_null_column.reference b/tests/queries/0_stateless/02013_json_function_null_column.reference new file mode 100644 index 00000000000..fdf3cea38fd --- /dev/null +++ b/tests/queries/0_stateless/02013_json_function_null_column.reference @@ -0,0 +1 @@ +\N \N \N diff --git a/tests/queries/0_stateless/02013_json_function_null_column.sql b/tests/queries/0_stateless/02013_json_function_null_column.sql new file mode 100644 index 00000000000..bb2eed84b8f --- /dev/null +++ b/tests/queries/0_stateless/02013_json_function_null_column.sql @@ -0,0 +1,8 @@ +drop table if exists test_table; + +create table test_table (col String, col_nullable Nullable(String)) engine MergeTree order by col; +insert into test_table select '{"string_value":null}' as col, '{"string_value":null}' as col_nullable; + +select JSONExtract(col, 'string_value', 'Nullable(String)') as res1, JSONExtract(col_nullable, 'string_value', 'Nullable(String)') as res2, JSONExtract(assumeNotNull(col_nullable), 'string_value', 'Nullable(String)') as res3 from test_table; + +drop table test_table;