diff --git a/be/src/vec/functions/function_string.cpp b/be/src/vec/functions/function_string.cpp index 8aee837ac63fbb..7c5cbaa8854a69 100644 --- a/be/src/vec/functions/function_string.cpp +++ b/be/src/vec/functions/function_string.cpp @@ -1131,6 +1131,7 @@ void register_function_string(SimpleFunctionFactory& factory) { factory.register_function>(); factory.register_function>(); factory.register_function>(); + factory.register_function(); /// @TEMPORARY: for be_exec_version=3 factory.register_alternative_function>(); diff --git a/be/src/vec/functions/function_string.h b/be/src/vec/functions/function_string.h index 9ae686f3398dd2..3ca13a270c620a 100644 --- a/be/src/vec/functions/function_string.h +++ b/be/src/vec/functions/function_string.h @@ -294,6 +294,62 @@ struct SubstringUtil { } }; +class FunctionStrcmp : public IFunction { +public: + static constexpr auto name = "strcmp"; + + static FunctionPtr create() { return std::make_shared(); } + + String get_name() const override { return name; } + + size_t get_number_of_arguments() const override { return 2; } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + return std::make_shared(); + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) const override { + const auto& arg0_column = + assert_cast(*block.get_by_position(arguments[0]).column); + const auto& arg1_column = + assert_cast(*block.get_by_position(arguments[1]).column); + + auto result_column = ColumnInt16::create(input_rows_count); + auto& result_data = result_column->get_data(); + auto null_column = ColumnUInt8::create(input_rows_count); + auto& null_map = null_column->get_data(); + + for (int row = 0; row < input_rows_count; row++) { + null_map[row] = false; + if (arg0_column.is_nullable() || arg1_column.is_nullable()) { + null_map[row] = true; + continue; + } + + auto arg0 = arg0_column.get_data_at(row).to_string(); + auto arg1 = arg1_column.get_data_at(row).to_string(); + + Int16* result_cell = &result_data[row]; + *result_cell = strcmp(arg0.c_str(), arg1.c_str()); + } + + block.replace_by_position( + result, ColumnNullable::create(std::move(result_column), std::move(null_column))); + return Status::OK(); + } + + static int strcmp(const char* arg0, const char* arg1) { + int ret = std::strcmp(arg0, arg1); + if (ret < 0) { + return -1; + } else if (ret > 0) { + return 1; + } + return 0; + } +}; + struct SubstringUtilOld { static constexpr auto name = "substring"; 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 d28cb751eaa03d..328f6486eab05b 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 @@ -383,6 +383,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StrLeft; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrRight; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp; import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace; @@ -628,6 +629,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(InnerProduct.class, "inner_product"), scalar(Instr.class, "instr"), scalar(InttoUuid.class, "int_to_uuid"), + scalar(Strcmp.class, "strcmp"), scalar(Ipv4NumToString.class, "ipv4_num_to_string", "inet_ntoa"), scalar(Ipv4StringToNum.class, "ipv4_string_to_num"), scalar(Ipv4StringToNumOrDefault.class, "ipv4_string_to_num_or_default"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Strcmp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Strcmp.java new file mode 100644 index 00000000000000..aa1f6c5506fda4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Strcmp.java @@ -0,0 +1,70 @@ +// 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.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.SmallIntType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'strcmp'. + */ +public class Strcmp extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, PropagateNullable { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(SmallIntType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(SmallIntType.INSTANCE).args(StringType.INSTANCE, StringType.INSTANCE)); + + /** + * constructor with 2 argument. + */ + public Strcmp(Expression arg0, Expression arg1) { + super("strcmp", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public Strcmp withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new Strcmp(children.get(0), children.get(1)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStrcmp(this, context); + } +} 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 83a4a2aa027520..8788ebb33d09cf 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 @@ -381,6 +381,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StrLeft; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrRight; import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp; import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace; @@ -2011,6 +2012,10 @@ default R visitInttoUuid(InttoUuid inttoUuid, C context) { return visitScalarFunction(inttoUuid, context); } + default R visitStrcmp(Strcmp strcmp, C context) { + return visitScalarFunction(strcmp, context); + } + default R visitVersion(Version version, C context) { return visitScalarFunction(version, context); } diff --git a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out index e3ca494c632fb9..f2f00df695432b 100644 Binary files a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out and b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out differ diff --git a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy index 2f5b49aa22dc6a..89a8dccd9ad9d0 100644 --- a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy @@ -285,6 +285,20 @@ suite("test_string_function", "arrow_flight_sql") { qt_sql "select substring_index(\"prefix_string\", \"_\", null);" qt_sql "select substring_index(\"prefix_string\", \"__\", -1);" + qt_sql_func_strcmp "select strcmp('test', 'test1');" + qt_sql_func_strcmp "select strcmp('test1', 'test');" + qt_sql_func_strcmp "select strcmp('test', 'test');" + qt_sql_func_strcmp "select strcmp('', '');" + qt_sql_func_strcmp "select strcmp(null, 'test');" + qt_sql_func_strcmp "select strcmp('test', null);" + qt_sql_func_strcmp "select strcmp(\"test\", \"test1\");" + qt_sql_func_strcmp "select strcmp(\"test1\", \"test\");" + qt_sql_func_strcmp "select strcmp(\"test\", \"test\");" + qt_sql_func_strcmp "select strcmp(\"\", \"\");" + qt_sql_func_strcmp "select strcmp(null, \"test\");" + qt_sql_func_strcmp "select strcmp(\"test\", null);" + qt_sql_func_strcmp "select strcmp(null, null);" + sql 'set enable_nereids_planner=true' sql 'set enable_fallback_to_original_planner=false'