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

Reply via email to