You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by ya...@apache.org on 2021/09/02 01:59:20 UTC

[incubator-doris] branch master updated: [Feature]Support functions of json_array, json_object, json_quote (#6504)

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 <87...@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-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org