From 50eeb2d9a4f67184974b383ef2b6f8769afc2f40 Mon Sep 17 00:00:00 2001 From: xueweizhang Date: Sat, 25 Mar 2023 21:57:29 +0800 Subject: [PATCH] [fix](json) change int to bigint for json function (#17769) --- be/src/vec/functions/function_json.cpp | 45 ++++++++++- be/test/vec/function/function_json_test.cpp | 13 ++++ .../json-functions/get_json_bigint.md | 76 +++++++++++++++++++ .../json-functions/get_json_bigint.md | 76 +++++++++++++++++++ .../doris/analysis/FunctionCallExpr.java | 8 +- .../doris/catalog/BuiltinScalarFunctions.java | 2 + .../functions/scalar/GetJsonBigInt.java | 71 +++++++++++++++++ .../visitor/ScalarFunctionVisitor.java | 5 ++ gensrc/script/doris_builtins_functions.py | 2 + .../json_functions/test_json_function.out | 17 ++++- .../json_functions/test_json_function.groovy | 7 +- 11 files changed, 314 insertions(+), 8 deletions(-) create mode 100644 docs/en/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md create mode 100644 docs/zh-CN/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/GetJsonBigInt.java diff --git a/be/src/vec/functions/function_json.cpp b/be/src/vec/functions/function_json.cpp index 9a22fc94f0..1a51559dcd 100644 --- a/be/src/vec/functions/function_json.cpp +++ b/be/src/vec/functions/function_json.cpp @@ -261,6 +261,9 @@ struct GetJsonNumberType { } else if constexpr (std::is_same_v) { root = get_json_object(json_string, path_string, &document); handle_result(root, res[i], null_map[i]); + } else if constexpr (std::is_same_v) { + root = get_json_object(json_string, path_string, &document); + handle_result(root, res[i], null_map[i]); } } } @@ -272,6 +275,8 @@ struct GetJsonNumberType { res_null = 1; } else if (root->IsInt()) { res = root->GetInt(); + } else if (root->IsInt64()) { + res = root->GetInt64(); } else if (root->IsDouble()) { res = root->GetDouble(); } else { @@ -287,6 +292,15 @@ struct GetJsonNumberType { res_null = 1; } } + + template , T>* = nullptr> + static void handle_result(rapidjson::Value* root, int64_t& res, uint8_t& res_null) { + if (root != nullptr && root->IsInt64()) { + res = root->GetInt64(); + } else { + res_null = 1; + } + } }; // Helper Class @@ -302,6 +316,12 @@ struct JsonNumberTypeInt { using ColumnType = ColumnVector; }; +struct JsonNumberTypeBigInt { + using T = int64_t; + using ReturnType = DataTypeInt64; + using ColumnType = ColumnVector; +}; + struct GetJsonDouble : public GetJsonNumberType { static constexpr auto name = "get_json_double"; using ReturnType = typename JsonNumberTypeDouble::ReturnType; @@ -314,6 +334,12 @@ struct GetJsonInt : public GetJsonNumberType { using ColumnType = typename JsonNumberTypeInt::ColumnType; }; +struct GetJsonBigInt : public GetJsonNumberType { + static constexpr auto name = "get_json_bigint"; + using ReturnType = typename JsonNumberTypeBigInt::ReturnType; + using ColumnType = typename JsonNumberTypeBigInt::ColumnType; +}; + struct GetJsonString { static constexpr auto name = "get_json_string"; using ReturnType = DataTypeString; @@ -398,7 +424,7 @@ struct JsonParser<'2'> { // int static void update_value(StringParser::ParseResult& result, rapidjson::Value& value, StringRef data, rapidjson::Document::AllocatorType& allocator) { - value.SetInt(StringParser::string_to_int(data.data, data.size, &result)); + value.SetInt(StringParser::string_to_int(data.data, data.size, &result)); } }; @@ -421,6 +447,15 @@ struct JsonParser<'4'> { } }; +template <> +struct JsonParser<'5'> { + // bigint + static void update_value(StringParser::ParseResult& result, rapidjson::Value& value, + StringRef data, rapidjson::Document::AllocatorType& allocator) { + value.SetInt64(StringParser::string_to_int(data.data, data.size, &result)); + } +}; + template struct ExecuteReducer { template @@ -441,7 +476,7 @@ struct FunctionJsonArrayImpl { rapidjson::Document::AllocatorType& allocator, const std::vector& nullmaps) { for (int i = 0; i < data_columns.size() - 1; i++) { - constexpr_int_match<'0', '5', Reducer>::run(type_flags[i], objects, allocator, + constexpr_int_match<'0', '6', Reducer>::run(type_flags[i], objects, allocator, data_columns[i], nullmaps[i]); } } @@ -481,7 +516,7 @@ struct FunctionJsonObjectImpl { } for (int i = 0; i + 1 < data_columns.size() - 1; i += 2) { - constexpr_int_match<'0', '5', Reducer>::run(type_flags[i + 1], objects, allocator, + constexpr_int_match<'0', '6', Reducer>::run(type_flags[i + 1], objects, allocator, data_columns[i], data_columns[i + 1], nullmaps[i + 1]); } @@ -496,7 +531,7 @@ struct FunctionJsonObjectImpl { rapidjson::Value key; rapidjson::Value value; for (int i = 0; i < objects.size(); i++) { - JsonParser<'5'>::update_value(result, key, key_column->get_data_at(i), + JsonParser<'6'>::update_value(result, key, key_column->get_data_at(i), allocator); // key always is string if (nullmap != nullptr && nullmap->get_data()[i]) { JsonParser<'0'>::update_value(result, value, value_column->get_data_at(i), @@ -653,6 +688,7 @@ public: using FunctionGetJsonDouble = FunctionBinaryStringOperateToNullType; using FunctionGetJsonInt = FunctionBinaryStringOperateToNullType; +using FunctionGetJsonBigInt = FunctionBinaryStringOperateToNullType; using FunctionGetJsonString = FunctionBinaryStringOperateToNullType; class FunctionJsonValid : public IFunction { @@ -720,6 +756,7 @@ public: void register_function_json(SimpleFunctionFactory& factory) { factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_function(); diff --git a/be/test/vec/function/function_json_test.cpp b/be/test/vec/function/function_json_test.cpp index 0f87e45c11..f2947e837d 100644 --- a/be/test/vec/function/function_json_test.cpp +++ b/be/test/vec/function/function_json_test.cpp @@ -49,6 +49,19 @@ TEST(FunctionJsonTEST, GetJsonIntTest) { check_function(func_name, input_types, data_set); } +TEST(FunctionJsonTEST, GetJsonBigIntTest) { + std::string func_name = "get_json_bigint"; + InputTypeSet input_types = {TypeIndex::String, TypeIndex::String}; + DataSet data_set = { + {{VARCHAR("{\"k1\":1, \"k2\":2}"), VARCHAR("$.k1")}, Int64(1)}, + {{VARCHAR("{\"k1\":1678708107000, \"k2\":2}"), VARCHAR("$.k1")}, Int64(1678708107000)}, + {{VARCHAR("{\"k1\":\"v1\", \"my.key\":[1, 2, 3]}"), VARCHAR("$.\"my.key\"[1]")}, + Int64(2)}, + {{VARCHAR("{\"k1.key\":{\"k2\":[1, 2]}}"), VARCHAR("$.\"k1.key\".k2[0]")}, Int64(1)}}; + + check_function(func_name, input_types, data_set); +} + TEST(FunctionJsonTEST, GetJsonStringTest) { std::string func_name = "get_json_string"; InputTypeSet input_types = {TypeIndex::String, TypeIndex::String}; diff --git a/docs/en/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md b/docs/en/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md new file mode 100644 index 0000000000..24be5a313d --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md @@ -0,0 +1,76 @@ +--- +{ + "title": "get_json_bigint", + "language": "en" +} +--- + + + +## get_json_bigint +### Description +#### Syntax + +`INT get_json_bigint(VARCHAR json_str, VARCHAR json_path)` + + +Parse and retrieve the big integer content of the specified path in the JSON string. +Where json_path must start with the $symbol and use. as the path splitter. If the path contains..., double quotation marks can be used to surround it. +Use [] to denote array subscripts, starting at 0. +The content of path cannot contain ",[and]. +If the json_string format is incorrect, or the json_path format is incorrect, or matches cannot be found, NULL is returned. + +In addition, it is recommended to use the jsonb type and jsonb_extract_XXX function performs the same function. + +### example + +1. Get the value of key as "k1" + +``` +mysql> SELECT get_json_bigint('{"k1":1, "k2":"2"}', "$.k1"); ++-----------------------------------------------+ +| get_json_bigint('{"k1":1, "k2":"2"}', '$.k1') | ++-----------------------------------------------+ +| 1 | ++-----------------------------------------------+ +``` + +2. Get the second element of the array whose key is "my. key" + +``` +mysql> SELECT get_json_bigint('{"k1":"v1", "my.key":[1, 1678708107000, 3]}', '$."my.key"[1]'); ++---------------------------------------------------------------------------------+ +| get_json_bigint('{"k1":"v1", "my.key":[1, 1678708107000, 3]}', '$."my.key"[1]') | ++---------------------------------------------------------------------------------+ +| 1678708107000 | ++---------------------------------------------------------------------------------+ +``` + +3. Get the first element in an array whose secondary path is k1. key - > K2 +``` +mysql> SELECT get_json_bigint('{"k1.key":{"k2":[1678708107000, 2]}}', '$."k1.key".k2[0]'); ++-----------------------------------------------------------------------------+ +| get_json_bigint('{"k1.key":{"k2":[1678708107000, 2]}}', '$."k1.key".k2[0]') | ++-----------------------------------------------------------------------------+ +| 1678708107000 | ++-----------------------------------------------------------------------------+ +``` +### keywords +GET_JSON_BIGINT,GET,JSON,BIGINT diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md new file mode 100644 index 0000000000..cfad402fc5 --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/get_json_bigint.md @@ -0,0 +1,76 @@ +--- +{ + "title": "get_json_bigint", + "language": "zh-CN" +} +--- + + + +## get_json_bigint +### description +#### Syntax + +`INT get_json_bigint(VARCHAR json_str, VARCHAR json_path)` + + +解析并获取 json 字符串内指定路径的整型(BIGINT)内容。 +其中 json_path 必须以 $ 符号作为开头,使用 . 作为路径分割符。如果路径中包含 . ,则可以使用双引号包围。 +使用 [ ] 表示数组下标,从 0 开始。 +path 的内容不能包含 ", [ 和 ]。 +如果 json_string 格式不对,或 json_path 格式不对,或无法找到匹配项,则返回 NULL。 + +另外,推荐使用jsonb类型和jsonb_extract_XXX函数实现同样的功能。 + +### example + +1. 获取 key 为 "k1" 的 value + +``` +mysql> SELECT get_json_bigint('{"k1":1, "k2":"2"}', "$.k1"); ++-----------------------------------------------+ +| get_json_bigint('{"k1":1, "k2":"2"}', '$.k1') | ++-----------------------------------------------+ +| 1 | ++-----------------------------------------------+ +``` + +2. 获取 key 为 "my.key" 的数组中第二个元素 + +``` +mysql> SELECT get_json_bigint('{"k1":"v1", "my.key":[1, 1678708107000, 3]}', '$."my.key"[1]'); ++---------------------------------------------------------------------------------+ +| get_json_bigint('{"k1":"v1", "my.key":[1, 1678708107000, 3]}', '$."my.key"[1]') | ++---------------------------------------------------------------------------------+ +| 1678708107000 | ++---------------------------------------------------------------------------------+ +``` + +3. 获取二级路径为 k1.key -> k2 的数组中,第一个元素 +``` +mysql> SELECT get_json_bigint('{"k1.key":{"k2":[1678708107000, 2]}}', '$."k1.key".k2[0]'); ++-----------------------------------------------------------------------------+ +| get_json_bigint('{"k1.key":{"k2":[1678708107000, 2]}}', '$."k1.key".k2[0]') | ++-----------------------------------------------------------------------------+ +| 1678708107000 | ++-----------------------------------------------------------------------------+ +``` +### keywords +GET_JSON_BIGINT,GET,JSON,BIGINT 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 fad40b8769..cfb85e8b86 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 @@ -415,13 +415,17 @@ public class FunctionCallExpr extends Expr { } else if (type.isBoolean()) { return 1; } else if (type.isFixedPointType()) { - return 2; + if (type.isInteger32Type()) { + return 2; + } else { + return 5; + } } else if (type.isFloatingPointType() || type.isDecimalV2() || type.isDecimalV3()) { return 3; } else if (type.isTime()) { return 4; } else { - return 5; + return 6; } } 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 e060b13905..57ef13813c 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 @@ -135,6 +135,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Fpow; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromBase64; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromDays; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromUnixtime; +import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonBigInt; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonDouble; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonInt; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonString; @@ -452,6 +453,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(FromBase64.class, "from_base64"), scalar(FromDays.class, "from_days"), scalar(FromUnixtime.class, "from_unixtime"), + scalar(GetJsonBigInt.class, "get_json_bigint"), scalar(GetJsonDouble.class, "get_json_double"), scalar(GetJsonInt.class, "get_json_int"), scalar(GetJsonString.class, "get_json_string"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/GetJsonBigInt.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/GetJsonBigInt.java new file mode 100644 index 0000000000..f788d4506f --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/GetJsonBigInt.java @@ -0,0 +1,71 @@ +// 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.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.IntegerType; +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 'get_json_int'. This class is generated by GenerateFunction. + */ +public class GetJsonBigInt extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, AlwaysNullable { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(IntegerType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(IntegerType.INSTANCE).args(StringType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 2 arguments. + */ + public GetJsonBigInt(Expression arg0, Expression arg1) { + super("get_json_bigint", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public GetJsonBigInt withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new GetJsonBigInt(children.get(0), children.get(1)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitGetJsonBigInt(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 9f549ed4cc..d345678e2f 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 @@ -138,6 +138,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Fpow; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromBase64; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromDays; import org.apache.doris.nereids.trees.expressions.functions.scalar.FromUnixtime; +import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonBigInt; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonDouble; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonInt; import org.apache.doris.nereids.trees.expressions.functions.scalar.GetJsonString; @@ -853,6 +854,10 @@ public interface ScalarFunctionVisitor { return visitScalarFunction(getJsonInt, context); } + default R visitGetJsonBigInt(GetJsonBigInt getJsonBigInt, C context) { + return visitScalarFunction(getJsonBigInt, context); + } + default R visitGetJsonString(GetJsonString getJsonString, C context) { return visitScalarFunction(getJsonString, context); } diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 95d48f9bac..0fa11adaee 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -1535,6 +1535,8 @@ visible_functions = [ [['get_json_int'], 'INT', ['STRING', 'STRING'], 'ALWAYS_NULLABLE'], [['get_json_double'], 'DOUBLE', ['STRING', 'STRING'], 'ALWAYS_NULLABLE'], [['get_json_string'], 'STRING', ['STRING', 'STRING'], 'ALWAYS_NULLABLE'], + [['get_json_bigint'], 'BIGINT', ['VARCHAR', 'VARCHAR'], 'ALWAYS_NULLABLE'], + [['get_json_bigint'], 'BIGINT', ['STRING', 'STRING'], 'ALWAYS_NULLABLE'], [['json_array'], 'VARCHAR', ['VARCHAR', '...'], 'ALWAYS_NOT_NULLABLE'], [['json_object'], 'VARCHAR', ['VARCHAR', '...'], 'ALWAYS_NOT_NULLABLE'], diff --git a/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out b/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out index 0a130c914d..e9a65fcc35 100644 --- a/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out +++ b/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out @@ -17,6 +17,15 @@ -- !sql -- 1 +-- !sql -- +1678708107000 + +-- !sql -- +1678708107002 + +-- !sql -- +1678708107001 + -- !sql -- v1 @@ -35,6 +44,9 @@ v1 -- !sql -- [1,"abc",null,true,"10:00:00"] +-- !sql -- +[1,"abc",null,true,"10:00:00",1678708107000] + -- !sql -- ["a",null,"c"] @@ -47,6 +59,9 @@ v1 -- !sql -- {"id":87,"name":"carrot"} +-- !sql -- +{"id":1678708107000,"name":"carrot"} + -- !sql -- ["a",null,"c"] @@ -54,7 +69,7 @@ v1 "null" "\\"null\\"" -- !sql -- -"[1, 2, 3]" +"[1, 2, 3, 1678708107000]" -- !sql -- \N diff --git a/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy index 751278cdca..7a403b549e 100644 --- a/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy @@ -24,6 +24,9 @@ suite("test_json_function") { qt_sql "SELECT get_json_int('{\"k1\":1, \"k2\":\"2\"}', \"\$.k1\");" qt_sql "SELECT get_json_int('{\"k1\":\"v1\", \"my.key\":[1, 2, 3]}', '\$.\"my.key\"[1]');" qt_sql "SELECT get_json_int('{\"k1.key\":{\"k2\":[1, 2]}}', '\$.\"k1.key\".k2[0]');" + qt_sql "SELECT get_json_bigint('{\"k1\":1678708107000, \"k2\":\"2\"}', \"\$.k1\");" + qt_sql "SELECT get_json_bigint('{\"k1\":\"v1\", \"my.key\":[11678708107001, 1678708107002, 31678708107003]}', '\$.\"my.key\"[1]');" + qt_sql "SELECT get_json_bigint('{\"k1.key\":{\"k2\":[1678708107001, 1678708107002]}}', '\$.\"k1.key\".k2[0]');" qt_sql "SELECT get_json_string('{\"k1\":\"v1\", \"k2\":\"v2\"}', \"\$.k1\");" qt_sql "SELECT get_json_string('{\"k1\":\"v1\", \"my.key\":[\"e1\", \"e2\", \"e3\"]}', '\$.\"my.key\"[1]');" @@ -32,15 +35,17 @@ suite("test_json_function") { qt_sql "SELECT json_array();" qt_sql "SELECT json_array(null);" qt_sql "SELECT json_array(1, \"abc\", NULL, TRUE, '10:00:00');" + qt_sql "SELECT json_array(1, \"abc\", NULL, TRUE, '10:00:00', 1678708107000);" qt_sql "SELECT json_array(\"a\", null, \"c\");" qt_sql "SELECT json_object();" qt_sql "SELECT json_object('time','10:00:00');" qt_sql "SELECT json_object('id', 87, 'name', 'carrot');" + qt_sql "SELECT json_object('id', 1678708107000, 'name', 'carrot');" qt_sql "SELECT json_array(\"a\", null, \"c\");" qt_sql "SELECT json_quote('null'), json_quote('\"null\"');" - qt_sql "SELECT json_quote('[1, 2, 3]');" + qt_sql "SELECT json_quote('[1, 2, 3, 1678708107000]');" qt_sql "SELECT json_quote(null);" qt_sql "SELECT json_quote(\"\\n\\b\\r\\t\");"