diff --git a/be/src/vec/functions/function_string.cpp b/be/src/vec/functions/function_string.cpp index 8aee837ac6..69f8699b5c 100644 --- a/be/src/vec/functions/function_string.cpp +++ b/be/src/vec/functions/function_string.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "common/status.h" @@ -57,6 +58,30 @@ struct StringASCII { } }; +struct NameQuote { + static constexpr auto name = "quote"; +}; + +struct NameQuoteImpl { + static Status vector(const ColumnString::Chars& data, const ColumnString::Offsets& offsets, + ColumnString::Chars& res_data, ColumnString::Offsets& res_offsets) { + size_t offset_size = offsets.size(); + size_t pos = 0; + res_offsets.resize(offset_size); + res_data.resize(data.size() + offset_size * 2); + for (int i = 0; i < offset_size; i++) { + const unsigned char* raw_str = &data[offsets[i - 1]]; + ColumnString::Offset size = offsets[i] - offsets[i - 1]; + res_data[pos] = '\''; + std::memcpy(res_data.data() + pos + 1, raw_str, size); + res_data[pos + size + 1] = '\''; + pos += size + 2; + res_offsets[i] = pos; + } + return Status::OK(); + } +}; + struct NameStringLenght { static constexpr auto name = "length"; }; @@ -1052,6 +1077,8 @@ using FunctionStringLocate = using FunctionStringFindInSet = FunctionBinaryToType; +using FunctionQuote = FunctionStringToString; + using FunctionToLower = FunctionStringToString, NameToLower>; using FunctionToUpper = FunctionStringToString, NameToUpper>; @@ -1082,6 +1109,7 @@ void register_function_string(SimpleFunctionFactory& factory) { factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_function(); factory.register_function(); diff --git a/be/test/vec/function/function_string_test.cpp b/be/test/vec/function/function_string_test.cpp index d8d1a57b8e..2c0fecbb30 100644 --- a/be/test/vec/function/function_string_test.cpp +++ b/be/test/vec/function/function_string_test.cpp @@ -215,6 +215,18 @@ TEST(function_string_test, function_string_length_test) { static_cast(check_function(func_name, input_types, data_set)); } +TEST(function_string_test, function_string_quote_test) { + std::string func_name = "quote"; + InputTypeSet input_types = {TypeIndex::String}; + DataSet data_set = {{{std::string("hello")}, std::string(R"('hello')")}, + {{std::string("hello\t\n\nworld")}, std::string("'hello\t\n\nworld'")}, + {{std::string("HELLO,!^%")}, std::string("'HELLO,!^%'")}, + {{std::string("MYtestStr\\t\\n")}, std::string("'MYtestStr\\t\\n'")}, + {{std::string("")}, std::string("''")}, + {{Null()}, Null()}}; + static_cast(check_function(func_name, input_types, data_set)); +} + TEST(function_string_test, function_append_trailing_char_if_absent_test) { std::string func_name = "append_trailing_char_if_absent"; diff --git a/docs/en/docs/sql-manual/sql-functions/string-functions/quote.md b/docs/en/docs/sql-manual/sql-functions/string-functions/quote.md new file mode 100644 index 0000000000..06246341bd --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/string-functions/quote.md @@ -0,0 +1,47 @@ +--- +{ + "title": "QUOTE", + "language": "en" +} +--- + + + +## quote +### description +#### Syntax + +`VARCHAR quote(VARCHAR str)` + + +Output all the strings in the argument as is and wrap them with '' + +### example + +``` +mysql> select quote('hello world!\\t'); ++-------------------------+ +| quote('hello world!\t') | ++-------------------------+ +| 'hello world!\t' | ++-------------------------+ +``` +### keywords + QUOTE diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/quote.md b/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/quote.md new file mode 100644 index 0000000000..ecc0c7d40d --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/string-functions/quote.md @@ -0,0 +1,47 @@ +--- +{ + "title": "QUOTE", + "language": "zh-CN" +} +--- + + + +## quote +### description +#### Syntax + +`VARCHAR quote(VARCHAR str)` + + +将参数中所有的字符串按原样输出,并用''套起来 + +### example + +``` +mysql> select quote('hello world!\\t'); ++-------------------------+ +| quote('hello world!\t') | ++-------------------------+ +| 'hello world!\t' | ++-------------------------+ +``` +### keywords + QUOTE 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 d28cb751ea..d27b0f3a31 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 @@ -317,6 +317,7 @@ 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; import org.apache.doris.nereids.trees.expressions.functions.scalar.Quarter; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Quote; import org.apache.doris.nereids.trees.expressions.functions.scalar.Radians; import org.apache.doris.nereids.trees.expressions.functions.scalar.Random; import org.apache.doris.nereids.trees.expressions.functions.scalar.RandomBytes; @@ -868,6 +869,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(Unhex.class, "unhex"), scalar(UnixTimestamp.class, "unix_timestamp"), scalar(Upper.class, "ucase", "upper"), + scalar(Quote.class, "quote"), scalar(UrlDecode.class, "url_decode"), scalar(User.class, "user"), scalar(UtcTimestamp.class, "utc_timestamp"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quote.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quote.java new file mode 100644 index 0000000000..25f97e6e39 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quote.java @@ -0,0 +1,69 @@ +// 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.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +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 'quote'. This class is not generated by GenerateFunction. + */ +public class Quote extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, PropagateNullable { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(StringType.INSTANCE).args(StringType.INSTANCE)); + + /** + * constructor with 1 argument. + */ + public Quote(Expression arg) { + super("quote", arg); + } + + /** + * withChildren. + */ + @Override + public Quote withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new Quote(children.get(0)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitQuote(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 83a4a2aa02..baa801f778 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 @@ -316,6 +316,7 @@ 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; import org.apache.doris.nereids.trees.expressions.functions.scalar.Quarter; +import org.apache.doris.nereids.trees.expressions.functions.scalar.Quote; import org.apache.doris.nereids.trees.expressions.functions.scalar.Radians; import org.apache.doris.nereids.trees.expressions.functions.scalar.Random; import org.apache.doris.nereids.trees.expressions.functions.scalar.RandomBytes; @@ -1987,6 +1988,10 @@ public interface ScalarFunctionVisitor { return visitScalarFunction(upper, context); } + default R visitQuote(Quote quote, C context) { + return visitScalarFunction(quote, context); + } + default R visitUser(User user, C context) { return visitScalarFunction(user, context); } diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 3d87ab86fd..0e7615829d 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -1576,6 +1576,7 @@ visible_functions = { [['char_length', 'character_length'], 'INT', ['VARCHAR'], ''], [['lower', 'lcase'], 'VARCHAR', ['VARCHAR'], ''], [['upper', 'ucase'], 'VARCHAR', ['VARCHAR'], ''], + [['quote'], 'VARCHAR', ['VARCHAR'], ''], [['initcap'], 'VARCHAR', ['VARCHAR'], ''], [['trim'], 'VARCHAR', ['VARCHAR','VARCHAR'], ''], [['trim'], 'VARCHAR', ['VARCHAR'], ''], @@ -1638,6 +1639,7 @@ visible_functions = { [['char_length', 'character_length'], 'INT', ['STRING'], ''], [['lower', 'lcase'], 'STRING', ['STRING'], ''], [['upper', 'ucase'], 'STRING', ['STRING'], ''], + [['quote'], 'STRING', ['STRING'], ''], [['trim'], 'STRING', ['STRING'], ''], [['trim'], 'STRING', ['STRING','STRING'], ''], [['ltrim'], 'STRING', ['STRING'], ''],