This is an automated email from the ASF dual-hosted git repository. yangzhg pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-doris.git
The following commit(s) were added to refs/heads/master by this push: new 7a15e58 [Feature]Support functions of json_array, json_object, json_quote (#6504) 7a15e58 is described below commit 7a15e583a7da7b19f9ba9e873ff34b1e0a780182 Author: zhangstar333 <87313068+zhangstar...@users.noreply.github.com> AuthorDate: Thu Sep 2 09:59:02 2021 +0800 [Feature]Support functions of json_array, json_object, json_quote (#6504) --- be/src/exprs/json_functions.cpp | 93 +++++++++++++++++- be/src/exprs/json_functions.h | 10 ++ be/test/exprs/json_function_test.cpp | 67 ++++++++++++- .../sql-functions/string-functions/json_array.md | 70 ++++++++++++++ .../sql-functions/string-functions/json_object.md | 71 ++++++++++++++ .../sql-functions/string-functions/json_quote.md | 70 ++++++++++++++ .../sql-functions/string-functions/json_array.md | 70 ++++++++++++++ .../sql-functions/string-functions/json_object.md | 70 ++++++++++++++ .../sql-functions/string-functions/json_quote.md | 70 ++++++++++++++ .../apache/doris/analysis/FunctionCallExpr.java | 107 ++++++++++++++++++++- gensrc/script/doris_builtins_functions.py | 14 ++- 11 files changed, 705 insertions(+), 7 deletions(-) diff --git a/be/src/exprs/json_functions.cpp b/be/src/exprs/json_functions.cpp index 470902f..94e26ce 100644 --- a/be/src/exprs/json_functions.cpp +++ b/be/src/exprs/json_functions.cpp @@ -26,6 +26,7 @@ #include <boost/algorithm/string.hpp> #include <boost/tokenizer.hpp> +#include <iomanip> #include <sstream> #include <string> #include <string_view> @@ -34,11 +35,11 @@ #include "common/logging.h" #include "exprs/anyval_util.h" #include "exprs/expr.h" +#include "gutil/strings/stringpiece.h" #include "olap/olap_define.h" #include "rapidjson/error/en.h" #include "runtime/string_value.h" #include "runtime/tuple_row.h" - namespace doris { // static const re2::RE2 JSON_PATTERN("^([a-zA-Z0-9_\\-\\:\\s#\\|\\.]*)(?:\\[([0-9]+)\\])?"); @@ -108,6 +109,96 @@ DoubleVal JsonFunctions::get_json_double(FunctionContext* context, const StringV } } +StringVal JsonFunctions::json_array(FunctionContext* context, int num_args, + const StringVal* json_str) { + if (json_str->is_null) { + return StringVal::null(); + } + rapidjson::Value array_obj(rapidjson::kArrayType); + rapidjson::Document document; + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + //flag: The number it contains represents the type of previous parameters + const StringVal& flag = json_str[num_args - 1]; + DCHECK_EQ(num_args - 1, flag.len); + for (int i = 0; i < num_args - 1; ++i) { + const StringVal& arg = json_str[i]; + rapidjson::Value val = parse_str_with_flag(arg, flag, i, allocator); + array_obj.PushBack(val, allocator); + } + rapidjson::StringBuffer buf; + rapidjson::Writer<rapidjson::StringBuffer> writer(buf); + array_obj.Accept(writer); + return AnyValUtil::from_string_temp(context, std::string(buf.GetString())); +} + +StringVal JsonFunctions::json_object(FunctionContext* context, int num_args, + const StringVal* json_str) { + if (json_str->is_null) { + return StringVal::null(); + } + rapidjson::Document document(rapidjson::kObjectType); + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + const StringVal& flag = json_str[num_args - 1]; + document.SetObject(); + DCHECK_EQ(num_args - 1, flag.len); + for (int i = 1; i < num_args - 1; i = i + 2) { + const StringVal& arg = json_str[i]; + rapidjson::Value key(rapidjson::kStringType); + key.SetString((char*)json_str[i - 1].ptr, json_str[i - 1].len, allocator); + rapidjson::Value val = parse_str_with_flag(arg, flag, i, allocator); + document.AddMember(key, val, allocator); + } + rapidjson::StringBuffer buf; + rapidjson::Writer<rapidjson::StringBuffer> writer(buf); + document.Accept(writer); + return AnyValUtil::from_string_temp(context, std::string(buf.GetString())); +} + +rapidjson::Value JsonFunctions::parse_str_with_flag(const StringVal& arg, const StringVal& flag, + const int num, + rapidjson::Document::AllocatorType& allocator) { + rapidjson::Value val; + if (*(flag.ptr + num) == '0') { //null + rapidjson::Value nullObject(rapidjson::kNullType); + val = nullObject; + } else if (*(flag.ptr + num) == '1') { //bool + bool res = ((arg == "1") ? true : false); + val.SetBool(res); + } else if (*(flag.ptr + num) == '2') { //int + std::stringstream ss; + ss << arg.ptr; + int number = 0; + ss >> number; + val.SetInt(number); + } else if (*(flag.ptr + num) == '3') { //double + std::stringstream ss; + ss << arg.ptr; + double number = 0.0; + ss >> number; + val.SetDouble(number); + } else if (*(flag.ptr + num) == '4' || *(flag.ptr + num) == '5') { + StringPiece str((char*)arg.ptr, arg.len); + if (*(flag.ptr + num) == '4') str = str.substr(1, str.length() - 2); + val.SetString(str.data(), str.length(), allocator); + } else { + DCHECK(false) << "parse json type error with unknown type"; + } + return val; +} +StringVal JsonFunctions::json_quote(FunctionContext* context, const StringVal& json_str) { + if (json_str.is_null) { + return StringVal::null(); + } + rapidjson::Value array_obj(rapidjson::kObjectType); + rapidjson::Document document; + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + array_obj.SetString(rapidjson::StringRef((char*)json_str.ptr, json_str.len)); + rapidjson::StringBuffer buf; + rapidjson::Writer<rapidjson::StringBuffer> writer(buf); + array_obj.Accept(writer); + return AnyValUtil::from_string_temp(context, std::string(buf.GetString())); +} + rapidjson::Value* JsonFunctions::match_value(const std::vector<JsonPath>& parsed_paths, rapidjson::Value* document, rapidjson::Document::AllocatorType& mem_allocator, diff --git a/be/src/exprs/json_functions.h b/be/src/exprs/json_functions.h index 86ce673..c16d51c 100644 --- a/be/src/exprs/json_functions.h +++ b/be/src/exprs/json_functions.h @@ -88,6 +88,13 @@ public: const JsonFunctionType& fntype, rapidjson::Document* document); + static doris_udf::StringVal json_array(doris_udf::FunctionContext* context, int num_args, + const doris_udf::StringVal* json_str); + static doris_udf::StringVal json_object(doris_udf::FunctionContext* context, int num_args, + const doris_udf::StringVal* json_str); + static doris_udf::StringVal json_quote(doris_udf::FunctionContext* context, + const doris_udf::StringVal& json_str); + /** * The `document` parameter must be has parsed. * return Value Is Array object @@ -122,6 +129,9 @@ private: bool is_insert_null = false); static void get_parsed_paths(const std::vector<std::string>& path_exprs, std::vector<JsonPath>* parsed_paths); + static rapidjson::Value parse_str_with_flag(const StringVal& arg, const StringVal& flag, + const int num, + rapidjson::Document::AllocatorType& allocator); }; } // namespace doris #endif diff --git a/be/test/exprs/json_function_test.cpp b/be/test/exprs/json_function_test.cpp index d1c4a4f..354f36a 100644 --- a/be/test/exprs/json_function_test.cpp +++ b/be/test/exprs/json_function_test.cpp @@ -26,11 +26,11 @@ #include <string> #include "common/object_pool.h" +#include "exprs/anyval_util.h" #include "exprs/json_functions.h" #include "runtime/runtime_state.h" #include "util/logging.h" #include "util/stopwatch.hpp" - namespace doris { // mock @@ -106,6 +106,71 @@ TEST_F(JsonFunctionTest, string) { ASSERT_EQ(std::string(buf5_3.GetString()), "205705999"); } +TEST_F(JsonFunctionTest, json_quote) { + doris_udf::FunctionContext* context = new doris_udf::FunctionContext(); + + ASSERT_EQ(StringVal::null(), JsonFunctions::json_quote(context, StringVal::null())); + + doris_udf::StringVal res1 = JsonFunctions::json_quote(context, StringVal("null")); + ASSERT_EQ(std::string("\"null\""), std::string((char*)res1.ptr, res1.len)); + + doris_udf::StringVal res2 = JsonFunctions::json_quote(context, StringVal("[1, 2, 3]")); + ASSERT_EQ(std::string("\"[1, 2, 3]\""), std::string((char*)res2.ptr, res2.len)); + + doris_udf::StringVal res3 = JsonFunctions::json_quote(context, StringVal("\n\b\r\t")); + ASSERT_EQ(std::string("\"\\n\\b\\r\\t\""), std::string((char*)res3.ptr, res3.len)); + + doris_udf::StringVal res4 = JsonFunctions::json_quote(context, StringVal("\"")); + ASSERT_EQ(std::string("\"\\\"\""), std::string((char*)res4.ptr, res4.len)); + + doris_udf::StringVal json_str= {""}; + doris_udf::StringVal res5 = JsonFunctions::json_quote(context, json_str); + ASSERT_EQ(std::string("\"\""), std::string((char*)res5.ptr, res5.len)); + delete context; +} + +TEST_F(JsonFunctionTest, json_array) { + doris_udf::FunctionContext* context = new doris_udf::FunctionContext(); + + doris_udf::StringVal json_str1[2] = {"[1,2,3]", "5"}; + doris_udf::StringVal res1 = JsonFunctions::json_array(context, 2, json_str1); + ASSERT_EQ(std::string("[\"[1,2,3]\"]"), std::string((char*)res1.ptr, res1.len)); + + doris_udf::StringVal json_str2[4] = {"1", "abc", "null", "250"}; + doris_udf::StringVal res2 = JsonFunctions::json_array(context, 4, json_str2); + ASSERT_EQ(std::string("[1,\"abc\",null]"), std::string((char*)res2.ptr, res2.len)); + + doris_udf::StringVal json_str3[1]= {""}; + doris_udf::StringVal res3 = JsonFunctions::json_array(context, 1, json_str3); + ASSERT_EQ(std::string("[]"), std::string((char*)res3.ptr, res3.len)); + + doris_udf::StringVal json_str4[2]= {"null","0"}; + doris_udf::StringVal res4 = JsonFunctions::json_array(context, 2, json_str4); + ASSERT_EQ(std::string("[null]"), std::string((char*)res4.ptr, res4.len)); + delete context; +} + +TEST_F(JsonFunctionTest, json_object) { + doris_udf::FunctionContext* context = new doris_udf::FunctionContext(); + doris_udf::StringVal json_str1[3] = {"id", "87", "52"}; + doris_udf::StringVal res1 = JsonFunctions::json_object(context, 3, json_str1); + ASSERT_EQ(std::string("{\"id\":87}"), std::string((char*)res1.ptr, res1.len)); + + doris_udf::StringVal json_str2[5] = {"name", "Jack", "score", "[87,98,90]", "5555"}; + doris_udf::StringVal res2 = JsonFunctions::json_object(context, 5, json_str2); + ASSERT_EQ(std::string("{\"name\":\"Jack\",\"score\":\"[87,98,90]\"}"), + std::string((char*)res2.ptr, res2.len)); + + doris_udf::StringVal json_str3[3] = {"key", "null","50"}; + doris_udf::StringVal res3 = JsonFunctions::json_object(context, 3, json_str3); + ASSERT_EQ(std::string("{\"key\":null}"), std::string((char*)res3.ptr, res3.len)); + + doris_udf::StringVal json_str4[1]= {""}; + doris_udf::StringVal res4 = JsonFunctions::json_object(context, 1, json_str4); + ASSERT_EQ(std::string("{}"), std::string((char*)res4.ptr, res4.len)); + delete context; +} + TEST_F(JsonFunctionTest, int) { std::string json_string("{\"id\":\"name\",\"age\":11,\"money\":123000.789}"); std::string path_string("$.age"); diff --git a/docs/en/sql-reference/sql-functions/string-functions/json_array.md b/docs/en/sql-reference/sql-functions/string-functions/json_array.md new file mode 100644 index 0000000..cceb11c --- /dev/null +++ b/docs/en/sql-reference/sql-functions/string-functions/json_array.md @@ -0,0 +1,70 @@ +--- +{ + "title": "json_array", + "language": "en" +} +--- + +<!-- +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. +--> + +# json_array +## Description +### Syntax + +`VARCHAR json_array(VARCHAR,...)` + + +Generate a json array containing the specified values, return empty if no values + +## example + +``` +MySQL> select json_array(); ++--------------+ +| json_array() | ++--------------+ +| [] | ++--------------+ + +MySQL> select json_array(null); ++--------------------+ +| json_array('NULL') | ++--------------------+ +| [NULL] | ++--------------------+ + + +MySQL> SELECT json_array(1, "abc", NULL, TRUE, CURTIME()); ++-----------------------------------------------+ +| json_array(1, 'abc', 'NULL', TRUE, curtime()) | ++-----------------------------------------------+ +| [1, "abc", NULL, TRUE, "10:41:15"] | ++-----------------------------------------------+ + + +MySQL> select json_array("a", null, "c"); ++------------------------------+ +| json_array('a', 'NULL', 'c') | ++------------------------------+ +| ["a", NULL, "c"] | ++------------------------------+ +``` +## keyword +json_array diff --git a/docs/en/sql-reference/sql-functions/string-functions/json_object.md b/docs/en/sql-reference/sql-functions/string-functions/json_object.md new file mode 100644 index 0000000..917f172 --- /dev/null +++ b/docs/en/sql-reference/sql-functions/string-functions/json_object.md @@ -0,0 +1,71 @@ +--- +{ + "title": "json_object", + "language": "en" +} +--- + +<!-- +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. +--> + +# json_object +## Description +### Syntax + +`VARCHAR json_object(VARCHAR,...)` + + +Generate a json object containing the specified Key-Value, +an exception error is returned when Key is NULL or the number of parameters are odd. + +## example + +``` +MySQL> select json_object(); ++---------------+ +| json_object() | ++---------------+ +| {} | ++---------------+ + +MySQL> select json_object('time',curtime()); ++--------------------------------+ +| json_object('time', curtime()) | ++--------------------------------+ +| {"time": "10:49:18"} | ++--------------------------------+ + + +MySQL> SELECT json_object('id', 87, 'name', 'carrot'); ++-----------------------------------------+ +| json_object('id', 87, 'name', 'carrot') | ++-----------------------------------------+ +| {"id": 87, "name": "carrot"} | ++-----------------------------------------+ + + +MySQL> select json_object('username',null); ++---------------------------------+ +| json_object('username', 'NULL') | ++---------------------------------+ +| {"username": NULL} | ++---------------------------------+ +``` +## keyword +json_object diff --git a/docs/en/sql-reference/sql-functions/string-functions/json_quote.md b/docs/en/sql-reference/sql-functions/string-functions/json_quote.md new file mode 100644 index 0000000..e02168e --- /dev/null +++ b/docs/en/sql-reference/sql-functions/string-functions/json_quote.md @@ -0,0 +1,70 @@ +--- +{ + "title": "json_quote", + "language": "en" +} +--- + +<!-- +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. +--> + +# json_quote +## Description +### Syntax + +`VARCHAR json_quote(VARCHAR)` + + +Enclose json_value in double quotes ("), escape special characters contained. + +## example + +``` +MySQL> SELECT json_quote('null'), json_quote('"null"'); ++--------------------+----------------------+ +| json_quote('null') | json_quote('"null"') | ++--------------------+----------------------+ +| "null" | "\"null\"" | ++--------------------+----------------------+ + + +MySQL> SELECT json_quote('[1, 2, 3]'); ++-------------------------+ +| json_quote('[1, 2, 3]') | ++-------------------------+ +| "[1, 2, 3]" | ++-------------------------+ + + +MySQL> SELECT json_quote(null); ++------------------+ +| json_quote(null) | ++------------------+ +| NULL | ++------------------+ + +MySQL> select json_quote("\n\b\r\t"); ++------------------------+ +| json_quote('\n\b\r\t') | ++------------------------+ +| "\n\b\r\t" | ++------------------------+ +``` +## keyword +json_quote diff --git a/docs/zh-CN/sql-reference/sql-functions/string-functions/json_array.md b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_array.md new file mode 100644 index 0000000..ff6a622 --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_array.md @@ -0,0 +1,70 @@ +--- +{ + "title": "json_array", + "language": "zh-CN" +} +--- + +<!-- +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. +--> + +# json_array +## description +### Syntax + +`VARCHAR json_array(VARCHAR,...)` + + +生成一个包含指定元素的json数组,未指定时返回空数组 + +## example + +``` +MySQL> select json_array(); ++--------------+ +| json_array() | ++--------------+ +| [] | ++--------------+ + +MySQL> select json_array(null); ++--------------------+ +| json_array('NULL') | ++--------------------+ +| [NULL] | ++--------------------+ + + +MySQL> SELECT json_array(1, "abc", NULL, TRUE, CURTIME()); ++-----------------------------------------------+ +| json_array(1, 'abc', 'NULL', TRUE, curtime()) | ++-----------------------------------------------+ +| [1, "abc", NULL, TRUE, "10:41:15"] | ++-----------------------------------------------+ + + +MySQL> select json_array("a", null, "c"); ++------------------------------+ +| json_array('a', 'NULL', 'c') | ++------------------------------+ +| ["a", NULL, "c"] | ++------------------------------+ +``` +## keyword +json_array diff --git a/docs/zh-CN/sql-reference/sql-functions/string-functions/json_object.md b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_object.md new file mode 100644 index 0000000..0f3b1fe --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_object.md @@ -0,0 +1,70 @@ +--- +{ + "title": "json_object", + "language": "zh-CN" +} +--- + +<!-- +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. +--> + +# json_object +## description +### Syntax + +`VARCHAR json_object(VARCHAR,...)` + + +生成一个包含指定Key-Value对的json object, 当Key值为NULL或者传入参数为奇数个时,返回异常错误 + +## example + +``` +MySQL> select json_object(); ++---------------+ +| json_object() | ++---------------+ +| {} | ++---------------+ + +MySQL> select json_object('time',curtime()); ++--------------------------------+ +| json_object('time', curtime()) | ++--------------------------------+ +| {"time": "10:49:18"} | ++--------------------------------+ + + +MySQL> SELECT json_object('id', 87, 'name', 'carrot'); ++-----------------------------------------+ +| json_object('id', 87, 'name', 'carrot') | ++-----------------------------------------+ +| {"id": 87, "name": "carrot"} | ++-----------------------------------------+ + + +MySQL> select json_object('username',null); ++---------------------------------+ +| json_object('username', 'NULL') | ++---------------------------------+ +| {"username": NULL} | ++---------------------------------+ +``` +## keyword +json_object diff --git a/docs/zh-CN/sql-reference/sql-functions/string-functions/json_quote.md b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_quote.md new file mode 100644 index 0000000..67ed605 --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-functions/string-functions/json_quote.md @@ -0,0 +1,70 @@ +--- +{ + "title": "json_quote", + "language": "zh-CN" +} +--- + +<!-- +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. +--> + +# json_quote +## description +### Syntax + +`VARCHAR json_quote(VARCHAR)` + + +将json_value用双引号(")括起来,跳过其中包含的特殊转义字符 + +## example + +``` +MySQL> SELECT json_quote('null'), json_quote('"null"'); ++--------------------+----------------------+ +| json_quote('null') | json_quote('"null"') | ++--------------------+----------------------+ +| "null" | "\"null\"" | ++--------------------+----------------------+ + + +MySQL> SELECT json_quote('[1, 2, 3]'); ++-------------------------+ +| json_quote('[1, 2, 3]') | ++-------------------------+ +| "[1, 2, 3]" | ++-------------------------+ + + +MySQL> SELECT json_quote(null); ++------------------+ +| json_quote(null) | ++------------------+ +| NULL | ++------------------+ + +MySQL> select json_quote("\n\b\r\t"); ++------------------------+ +| json_quote('\n\b\r\t') | ++------------------------+ +| "\n\b\r\t" | ++------------------------+ +``` +## keyword +json_quote diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java index 18c8ddd..a624cb0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java @@ -44,6 +44,7 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Lists; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -53,7 +54,7 @@ import java.io.DataOutput; import java.io.IOException; import java.util.Arrays; import java.util.List; - +import java.text.StringCharacterIterator; // TODO: for aggregations, we need to unify the code paths for builtins and UDAs. public class FunctionCallExpr extends Expr { private static final Logger LOG = LogManager.getLogger(FunctionCallExpr.class); @@ -74,12 +75,14 @@ public class FunctionCallExpr extends Expr { .add("stddev").add("stddev_val").add("stddev_samp") .add("variance").add("variance_pop").add("variance_pop").add("var_samp").add("var_pop").build(); private static final String ELEMENT_EXTRACT_FN_NAME = "%element_extract%"; - + + //use to record the num of json_object parameters + private int originChildSize; // Save the functionCallExpr in the original statement private Expr originStmtFnExpr; private boolean isRewrote = false; - + public void setIsAnalyticFnCall(boolean v) { isAnalyticFnCall = v; } @@ -125,6 +128,7 @@ public class FunctionCallExpr extends Expr { this.isMergeAggFn = isMergeAggFn; if (params.exprs() != null) { children.addAll(params.exprs()); + originChildSize = children.size(); } } @@ -162,6 +166,31 @@ public class FunctionCallExpr extends Expr { fn = other.fn; } + public String parseJsonDataType(boolean useKeyCheck) throws AnalysisException { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < children.size(); ++i) { + Type type = getChild(i).getType(); + if (type.isNull()) { //Not to return NULL directly, so save string, but flag is '0' + if (((i & 1) == 0) && useKeyCheck == true) { + throw new AnalysisException("json_object key can't be NULL: " + this.toSql()); + } + children.set(i, new StringLiteral("NULL")); + sb.append("0"); + } else if (type.isBoolean()) { + sb.append("1"); + } else if (type.isFixedPointType()) { + sb.append("2"); + } else if (type.isFloatingPointType() || type.isDecimalV2()) { + sb.append("3"); + } else if (type.isTime()) { + sb.append("4"); + } else { + sb.append("5"); + } + } + return sb.toString(); + } + public boolean isMergeAggFn() { return isMergeAggFn; } @@ -209,8 +238,22 @@ public class FunctionCallExpr extends Expr { } if (((FunctionCallExpr) expr).fnParams.isDistinct()) { sb.append("DISTINCT "); + } + boolean isJsonFunction = false; + int len = children.size(); + List<String> result = Lists.newArrayList(); + if ((fnName.getFunction().equalsIgnoreCase("json_array")) || + (fnName.getFunction().equalsIgnoreCase("json_object"))) { + len = len - 1; + isJsonFunction = true; + } + for (int i = 0; i < len; ++i) { + result.add(children.get(i).toSql()); + } + sb.append(Joiner.on(", ").join(result)).append(")"); + if (fnName.getFunction().equalsIgnoreCase("json_quote") || isJsonFunction) { + return forJSON(sb.toString()); } - sb.append(Joiner.on(", ").join(expr.childrenToSql())).append(")"); return sb.toString(); } @@ -322,6 +365,25 @@ public class FunctionCallExpr extends Expr { } return; } + + if(fnName.getFunction().equalsIgnoreCase("json_array")) { + String res = parseJsonDataType(false); + if (children.size() == originChildSize) { + children.add(new StringLiteral(res)); + } + return; + } + + if(fnName.getFunction().equalsIgnoreCase("json_object")) { + if ((children.size()&1) == 1 && (originChildSize == children.size())) { + throw new AnalysisException("json_object can't be odd parameters, need even parameters: " + this.toSql()); + } + String res = parseJsonDataType(true); + if (children.size() == originChildSize) { + children.add(new StringLiteral(res)); + } + return; + } if (fnName.getFunction().equalsIgnoreCase("group_concat")) { if (children.size() > 2 || children.isEmpty()) { @@ -510,6 +572,7 @@ public class FunctionCallExpr extends Expr { } } } + } // Provide better error message for some aggregate builtins. These can be @@ -914,4 +977,40 @@ public class FunctionCallExpr extends Expr { result = 31 * result + Objects.hashCode(fnParams); return result; } + public String forJSON(String str){ + final StringBuilder result = new StringBuilder(); + StringCharacterIterator iterator = new StringCharacterIterator(str); + char character = iterator.current(); + while (character != StringCharacterIterator.DONE){ + if( character == '\"' ){ + result.append("\\\""); + } + else if(character == '\\'){ + result.append("\\\\"); + } + else if(character == '/'){ + result.append("\\/"); + } + else if(character == '\b'){ + result.append("\\b"); + } + else if(character == '\f'){ + result.append("\\f"); + } + else if(character == '\n'){ + result.append("\\n"); + } + else if(character == '\r'){ + result.append("\\r"); + } + else if(character == '\t'){ + result.append("\\t"); + } + else { + result.append(character); + } + character = iterator.next(); + } + return result.toString(); + } } diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 032b7ad..0a7a742 100755 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -1125,6 +1125,16 @@ visible_functions = [ '_ZN5doris13JsonFunctions15json_path_closeEPN9doris_udf15FunctionContextENS2_18FunctionStateScopeE', 'vec', ''], + [['json_array'], 'VARCHAR', ['VARCHAR', '...'], + '_ZN5doris13JsonFunctions10json_arrayEPN9doris_udf15FunctionContextEiPKNS1_9StringValE', + '', '', '', ''], + [['json_object'], 'VARCHAR', ['VARCHAR', '...'], + '_ZN5doris13JsonFunctions11json_objectEPN9doris_udf15FunctionContextEiPKNS1_9StringValE', + '', '', '', ''], + [['json_quote'], 'VARCHAR', ['VARCHAR'], + '_ZN5doris13JsonFunctions10json_quoteEPN9doris_udf15FunctionContextERKNS1_9StringValE', + '', '', '', ''], + #hll function [['hll_cardinality'], 'BIGINT', ['VARCHAR'], '_ZN5doris12HllFunctions15hll_cardinalityEPN9doris_udf15FunctionContextERKNS1_9StringValE', @@ -1330,7 +1340,9 @@ non_null_result_with_null_param_functions = [ 'nullif', 'null_or_empty', 'coalesce', - 'array' + 'array', + 'json_array', + 'json_object' ] # Nondeterministic functions may return different results each time they are called --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org