This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch branch-c108335-hive-sql in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-c108335-hive-sql by this push: new fcab5d6bf21 Impl printf (#49210) fcab5d6bf21 is described below commit fcab5d6bf213fca1ddd3e690d94e9ce4b7fa445a Author: Socrates <suyit...@selectdb.com> AuthorDate: Tue Mar 18 19:03:15 2025 +0800 Impl printf (#49210) --- be/src/vec/functions/function_string.cpp | 1 + be/src/vec/functions/function_string.h | 108 +++++++++++++++++++++ .../doris/catalog/BuiltinScalarFunctions.java | 2 + .../trees/expressions/functions/scalar/Printf.java | 83 ++++++++++++++++ .../expressions/visitor/ScalarFunctionVisitor.java | 5 + 5 files changed, 199 insertions(+) diff --git a/be/src/vec/functions/function_string.cpp b/be/src/vec/functions/function_string.cpp index 39f303e85a4..1c328d5d145 100644 --- a/be/src/vec/functions/function_string.cpp +++ b/be/src/vec/functions/function_string.cpp @@ -1245,6 +1245,7 @@ void register_function_string(SimpleFunctionFactory& factory) { factory.register_function<FunctionOverlay>(); factory.register_function<FunctionStrcmp>(); factory.register_function<FunctionNgramSearch>(); + factory.register_function<FunctionPrintf>(); factory.register_alias(FunctionLeft::name, "strleft"); factory.register_alias(FunctionRight::name, "strright"); diff --git a/be/src/vec/functions/function_string.h b/be/src/vec/functions/function_string.h index 4f5d1f6e9ba..2dafa1560e7 100644 --- a/be/src/vec/functions/function_string.h +++ b/be/src/vec/functions/function_string.h @@ -17,6 +17,8 @@ #pragma once +#include <fmt/core.h> +#include <fmt/printf.h> #include <sys/types.h> #include <algorithm> @@ -66,6 +68,7 @@ #include "vec/core/column_with_type_and_name.h" #include "vec/core/types.h" #include "vec/data_types/data_type.h" +#include "vec/functions/cast_type_to_either.h" #include "vec/utils/template_helpers.hpp" #ifndef USE_LIBCPP @@ -4958,4 +4961,109 @@ private: } }; +class FunctionPrintf : public IFunction { +public: + static constexpr auto name = "printf"; + + static FunctionPtr create() { return std::make_shared<FunctionPrintf>(); } + + String get_name() const override { return name; } + + size_t get_number_of_arguments() const override { return 0; } + bool is_variadic() const override { return true; } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + return remove_nullable(std::make_shared<DataTypeString>()); + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + uint32_t result, size_t input_rows_count) const override { + size_t num_element = arguments.size(); + auto result_col = block.get_by_position(result).type->create_column(); + + // first arg is format string, so it should be string type + auto format_str_type = block.get_by_position(arguments[0]).type; + if (format_str_type->get_type_id() != TypeIndex::String) { + return Status::InvalidArgument( + "Argument 1 of function PRINTF must be string, but {} was found.", + format_str_type->get_name()); + } + const auto* format_str_column = + assert_cast<const ColumnString*>(block.get_by_position(arguments[0]).column.get()); + std::vector<ColumnWithTypeAndName> format_arg_columns(num_element); + + for (size_t i = 0; i < num_element; ++i) { + format_arg_columns[i] = block.get_by_position(arguments[i]); + } + + for (size_t i = 0; i < input_rows_count; ++i) { + fmt::dynamic_format_arg_store<fmt::printf_context> store; + for (int j = 1; j < num_element; ++j) { + auto data = format_arg_columns[j].column->get_data_at(i); + auto type = format_arg_columns[j].type; + RETURN_IF_ERROR(handle_format_arg(data, type, store)); + } + auto format_str = format_str_column->get_data_at(i).to_string_view(); + try { + auto formatted = fmt::vsprintf(format_str, store); + result_col->insert_data(formatted.data(), formatted.size()); + } catch (const fmt::format_error& e) { + return Status::InvalidArgument("Failed to format string: {}, error: {}", format_str, + e.what()); + } + } + block.replace_by_position(result, std::move(result_col)); + return Status::OK(); + } + +private: + static Status handle_format_arg(const StringRef& data, const DataTypePtr& type, + fmt::dynamic_format_arg_store<fmt::printf_context>& store) { + switch (type->get_type_id()) { + case TypeIndex::Int64: + store.push_back(get_value_from_data<int64_t>(data)); + return Status::OK(); + case TypeIndex::Int32: + store.push_back(get_value_from_data<int32_t>(data)); + return Status::OK(); + case TypeIndex::Int16: + store.push_back(get_value_from_data<int16_t>(data)); + return Status::OK(); + case TypeIndex::Int8: + store.push_back(get_value_from_data<int8_t>(data)); + return Status::OK(); + case TypeIndex::UInt64: + store.push_back(get_value_from_data<uint64_t>(data)); + return Status::OK(); + case TypeIndex::UInt32: + store.push_back(get_value_from_data<uint32_t>(data)); + return Status::OK(); + case TypeIndex::UInt16: + store.push_back(get_value_from_data<uint16_t>(data)); + return Status::OK(); + case TypeIndex::UInt8: + store.push_back(get_value_from_data<uint8_t>(data)); + return Status::OK(); + case TypeIndex::Float64: + store.push_back(get_value_from_data<double>(data)); + return Status::OK(); + case TypeIndex::Float32: + store.push_back(get_value_from_data<float>(data)); + return Status::OK(); + case TypeIndex::String: + store.push_back(data.to_string()); + return Status::OK(); + default: + return Status::InvalidArgument("Unsupported printf type: {}", type->get_name()); + } + } + + template <typename T> + static T get_value_from_data(const StringRef& data) { + T value; + memcpy(&value, data.data, sizeof(value)); + return value; + } +}; + } // namespace doris::vectorized diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java index f31f705dafb..2cf23620b3e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java @@ -347,6 +347,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Pmod; import org.apache.doris.nereids.trees.expressions.functions.scalar.Positive; import org.apache.doris.nereids.trees.expressions.functions.scalar.Pow; import org.apache.doris.nereids.trees.expressions.functions.scalar.Power; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Printf; import org.apache.doris.nereids.trees.expressions.functions.scalar.Protocol; import org.apache.doris.nereids.trees.expressions.functions.scalar.QuantilePercent; import org.apache.doris.nereids.trees.expressions.functions.scalar.QuantileStateEmpty; @@ -844,6 +845,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(Positive.class, "positive"), scalar(Pow.class, "pow"), scalar(Power.class, "power"), + scalar(Printf.class, "printf"), scalar(Protocol.class, "protocol"), scalar(QuantilePercent.class, "quantile_percent"), scalar(QuantileStateEmpty.class, "quantile_state_empty"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Printf.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Printf.java new file mode 100644 index 00000000000..91a16958294 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Printf.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.util.ExpressionUtils; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Printf function + * printf(strfmt[, obj1, ...]) + * - strfmt: A STRING expression. + * - objN: A STRING or numeric expression. + * Returns a formatted string from printf-style format strings. + */ +public class Printf extends ScalarFunction + implements ExplicitlyCastableSignature, PropagateNullable { + + /** + * constructor with 1 or more arguments. + */ + public Printf(Expression arg0, Expression... varArgs) { + super("Printf", ExpressionUtils.mergeArguments(arg0, varArgs)); + } + + /** + * withChildren. + */ + @Override + public Printf withChildren(List<Expression> children) { + Preconditions.checkArgument(children.size() >= 1); + return new Printf(children.get(0), children.subList(1, children.size()).toArray(new Expression[0])); + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitPrintf(this, context); + } + + @Override + public List<FunctionSignature> getSignatures() { + Function<DataType, DataType> replaceDecimalWithDouble = dataType -> { + if (dataType.isDecimalLikeType()) { + return DoubleType.INSTANCE; + } + return dataType; + }; + List<DataType> argTypes = children.stream().map(ExpressionTrait::getDataType) + .map(replaceDecimalWithDouble) + .collect(Collectors.toList()); + return ImmutableList.of( + FunctionSignature.ret(StringType.INSTANCE).args(argTypes.toArray(new DataType[0]))); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java index e1af90013a1..6b0450c1d8b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java @@ -346,6 +346,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Pmod; import org.apache.doris.nereids.trees.expressions.functions.scalar.Positive; import org.apache.doris.nereids.trees.expressions.functions.scalar.Pow; import org.apache.doris.nereids.trees.expressions.functions.scalar.Power; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Printf; import org.apache.doris.nereids.trees.expressions.functions.scalar.Protocol; import org.apache.doris.nereids.trees.expressions.functions.scalar.QuantilePercent; import org.apache.doris.nereids.trees.expressions.functions.scalar.QuantileStateEmpty; @@ -1775,6 +1776,10 @@ public interface ScalarFunctionVisitor<R, C> { return visitScalarFunction(power, context); } + default R visitPrintf(Printf printf, C context) { + return visitScalarFunction(printf, context); + } + default R visitProtocol(Protocol protocol, C context) { return visitScalarFunction(protocol, context); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org