This is an automated email from the ASF dual-hosted git repository. kxiao pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/doris.git
commit 3c3f3c25398cf597fb067c58f9200f1aae23d5e4 Author: Liqf <[email protected]> AuthorDate: Wed Sep 20 10:40:44 2023 +0800 [feature](jsonb)support json_length and json_contains function (#24332) --- be/src/util/jsonb_document.h | 148 ++++++++++- be/src/vec/functions/function_json.cpp | 9 +- be/src/vec/functions/function_jsonb.cpp | 276 ++++++++++++++++++++- .../sql-functions/json-functions/json-contains.md | 79 ++++++ .../sql-functions/json-functions/json-length.md | 68 +++++ docs/sidebars.json | 2 + .../sql-functions/json-functions/json-contains.md | 79 ++++++ .../sql-functions/json-functions/json-length.md | 68 +++++ .../doris/catalog/BuiltinScalarFunctions.java | 4 + .../expressions/functions/scalar/JsonContains.java | 86 +++++++ .../expressions/functions/scalar/JsonLength.java | 86 +++++++ .../expressions/visitor/ScalarFunctionVisitor.java | 10 + gensrc/script/doris_builtins_functions.py | 10 +- .../data/jsonb_p0/test_jsonb_load_and_function.out | 262 +++++++++++++++++++ .../test_jsonb_load_unique_key_and_function.out | 238 ++++++++++++++++++ .../json_functions/test_json_function.out | 14 +- .../jsonb_p0/test_jsonb_load_and_function.groovy | 34 +++ .../test_jsonb_load_unique_key_and_function.groovy | 33 +++ 18 files changed, 1485 insertions(+), 21 deletions(-) diff --git a/be/src/util/jsonb_document.h b/be/src/util/jsonb_document.h index eab053cfb0..3150f0d7ee 100644 --- a/be/src/util/jsonb_document.h +++ b/be/src/util/jsonb_document.h @@ -361,6 +361,8 @@ public: leg_info* get_leg_from_leg_vector(size_t i) { return leg_vector[i].get(); } + void clean() { leg_vector.clear(); } + private: std::vector<std::unique_ptr<leg_info>> leg_vector; }; @@ -544,6 +546,12 @@ public: // size of the value in bytes unsigned int size() const; + //Get the number of jsonbvalue elements + int length() const; + + //Whether to include the jsonbvalue rhs + bool contains(JsonbValue* rhs) const; + // get the raw byte array of the value const char* getValuePtr() const; @@ -897,6 +905,40 @@ public: return end(); } + // Get number of elements in object + int numElem() const { + const char* pch = payload_; + const char* fence = payload_ + size_; + + unsigned int num = 0; + while (pch < fence) { + JsonbKeyValue* pkey = (JsonbKeyValue*)(pch); + ++num; + pch += pkey->numPackedBytes(); + } + + assert(pch == fence); + + return num; + } + + JsonbKeyValue* getJsonbKeyValue(unsigned int i) const { + const char* pch = payload_; + const char* fence = payload_ + size_; + + unsigned int num = 0; + while (pch < fence) { + JsonbKeyValue* pkey = (JsonbKeyValue*)(pch); + if (num == i) return pkey; + ++num; + pch += pkey->numPackedBytes(); + } + + assert(pch == fence); + + return nullptr; + } + JsonbValue* find(const char* key, hDictFind handler = nullptr) const { return const_cast<ObjectVal*>(this)->find(key, handler); } @@ -975,7 +1017,7 @@ public: } // Get number of elements in array - unsigned int numElem() const { + int numElem() const { const char* pch = payload_; const char* fence = payload_ + size_; @@ -1195,6 +1237,110 @@ inline unsigned int JsonbValue::size() const { } } +inline int JsonbValue::length() const { + switch (type_) { + case JsonbType::T_Int8: + case JsonbType::T_Int16: + case JsonbType::T_Int32: + case JsonbType::T_Int64: + case JsonbType::T_Double: + case JsonbType::T_Float: + case JsonbType::T_Int128: + case JsonbType::T_String: + case JsonbType::T_Binary: + case JsonbType::T_Null: + case JsonbType::T_True: + case JsonbType::T_False: { + return 1; + } + case JsonbType::T_Object: { + return ((ObjectVal*)this)->numElem(); + } + case JsonbType::T_Array: { + return ((ArrayVal*)this)->numElem(); + } + default: + return 0; + } +} + +inline bool JsonbValue::contains(JsonbValue* rhs) const { + switch (type_) { + case JsonbType::T_Int8: + case JsonbType::T_Int16: + case JsonbType::T_Int32: + case JsonbType::T_Int64: + case JsonbType::T_Int128: { + return ((JsonbIntVal*)(this))->val() == ((JsonbIntVal*)(rhs))->val(); + } + case JsonbType::T_Double: { + if (rhs->isDouble()) { + return ((JsonbDoubleVal*)(this))->val() == ((JsonbDoubleVal*)(rhs))->val(); + } + return false; + } + case JsonbType::T_Float: { + if (rhs->isDouble()) { + return ((JsonbFloatVal*)(this))->val() == ((JsonbFloatVal*)(rhs))->val(); + } + return false; + } + case JsonbType::T_String: + case JsonbType::T_Binary: { + if (rhs->isString()) { + auto str_value1 = (JsonbStringVal*)this; + auto str_value2 = (JsonbStringVal*)rhs; + return str_value1->length() == str_value2->length() && + std::memcmp(str_value1->getBlob(), str_value2->getBlob(), + str_value1->length()) == 0; + } + return false; + } + case JsonbType::T_Array: { + if (rhs->isArray() && ((ArrayVal*)this)->numElem() == ((ArrayVal*)rhs)->numElem() && + ((ArrayVal*)this)->numPackedBytes() == ((ArrayVal*)rhs)->numPackedBytes()) { + for (int i = 0; i < ((ArrayVal*)this)->numElem(); ++i) { + if (!((ArrayVal*)this)->get(i)->contains(((ArrayVal*)rhs)->get(i))) { + return false; + } + } + return true; + } + for (int i = 0; i < ((ArrayVal*)this)->numElem(); ++i) { + if (((ArrayVal*)this)->get(i)->contains(rhs)) { + return true; + } + } + return false; + } + case JsonbType::T_Object: { + if (rhs->isObject()) { + auto str_value1 = (ObjectVal*)this; + auto str_value2 = (ObjectVal*)rhs; + for (int i = 0; i < str_value2->numElem(); ++i) { + JsonbKeyValue* key = str_value2->getJsonbKeyValue(i); + JsonbValue* value = str_value1->find(key->getKeyStr(), key->klen()); + if (key != nullptr && value != nullptr && !key->value()->contains(value)) + return false; + } + return true; + } + return false; + } + case JsonbType::T_Null: { + return rhs->isNull(); + } + case JsonbType::T_True: { + return rhs->isTrue(); + } + case JsonbType::T_False: { + return rhs->isFalse(); + } + default: + return false; + } +} + inline const char* JsonbValue::getValuePtr() const { switch (type_) { case JsonbType::T_Int8: diff --git a/be/src/vec/functions/function_json.cpp b/be/src/vec/functions/function_json.cpp index 12af7f4bf1..7ea820b1cf 100644 --- a/be/src/vec/functions/function_json.cpp +++ b/be/src/vec/functions/function_json.cpp @@ -949,7 +949,12 @@ public: size_t get_number_of_arguments() const override { return 3; } DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { - return make_nullable(std::make_shared<DataTypeInt32>()); + return make_nullable(std::make_shared<DataTypeUInt8>()); + } + + DataTypes get_variadic_argument_types_impl() const override { + return {std::make_shared<DataTypeString>(), std::make_shared<DataTypeString>(), + std::make_shared<DataTypeString>()}; } bool use_default_implementation_for_nulls() const override { return false; } @@ -1022,7 +1027,7 @@ public: return Status::RuntimeError("Illegal column should be ColumnString"); } - auto col_to = ColumnVector<vectorized::Int32>::create(); + auto col_to = ColumnVector<vectorized::UInt8>::create(); auto& vec_to = col_to->get_data(); size_t size = col_json.size(); vec_to.resize(size); diff --git a/be/src/vec/functions/function_jsonb.cpp b/be/src/vec/functions/function_jsonb.cpp index eaedf5a69c..a354e2b23a 100644 --- a/be/src/vec/functions/function_jsonb.cpp +++ b/be/src/vec/functions/function_jsonb.cpp @@ -539,15 +539,6 @@ public: rdata.size())); } - // if not valid json path , should return error message to user - if (is_invalid_json_path) { - return Status::InvalidArgument( - "Json path error: {} for value: {}", - JsonbErrMsg::getErrMsg(JsonbErrType::E_INVALID_JSON_PATH), - std::string_view(reinterpret_cast<const char*>(rdata.data()), - rdata.size())); - } - json_path_list[pi] = std::move(path); return Status::OK(); @@ -1011,6 +1002,268 @@ using FunctionJsonbExtractDouble = FunctionJsonbExtract<JsonbExtractDouble>; using FunctionJsonbExtractString = FunctionJsonbExtract<JsonbExtractString>; using FunctionJsonbExtractJsonb = FunctionJsonbExtract<JsonbExtractJsonb>; +template <typename Impl> +class FunctionJsonbLength : public IFunction { +public: + static constexpr auto name = "json_length"; + String get_name() const override { return name; } + static FunctionPtr create() { return std::make_shared<FunctionJsonbLength<Impl>>(); } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + return make_nullable(std::make_shared<DataTypeInt32>()); + } + DataTypes get_variadic_argument_types_impl() const override { + return Impl::get_variadic_argument_types(); + } + size_t get_number_of_arguments() const override { + return get_variadic_argument_types_impl().size(); + } + + bool use_default_implementation_for_nulls() const override { return false; } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) override { + return Impl::execute_impl(context, block, arguments, result, input_rows_count); + } +}; + +struct JsonbLengthUtil { + static Status jsonb_length_execute(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + DCHECK_GE(arguments.size(), 2); + ColumnPtr jsonb_data_column; + bool jsonb_data_const = false; + // prepare jsonb data column + std::tie(jsonb_data_column, jsonb_data_const) = + unpack_if_const(block.get_by_position(arguments[0]).column); + ColumnPtr path_column; + bool is_const = false; + std::tie(path_column, is_const) = + unpack_if_const(block.get_by_position(arguments[1]).column); + JsonbPath path; + if (is_const) { + auto path_value = path_column->get_data_at(0); + if (!path.seek(path_value.data, path_value.size)) { + return Status::InvalidArgument( + "Json path error: {} for value: {}", + JsonbErrMsg::getErrMsg(JsonbErrType::E_INVALID_JSON_PATH), + std::string_view(reinterpret_cast<const char*>(path_value.data), + path_value.size)); + } + } + auto null_map = ColumnUInt8::create(input_rows_count, 0); + auto return_type = block.get_data_type(result); + MutableColumnPtr res = return_type->create_column(); + + for (size_t i = 0; i < input_rows_count; ++i) { + if (jsonb_data_column->is_null_at(i) || path_column->is_null_at(i)) { + null_map->get_data()[i] = 1; + res->insert_data(nullptr, 0); + continue; + } + if (!is_const) { + auto path_value = path_column->get_data_at(i); + path.clean(); + if (!path.seek(path_value.data, path_value.size)) { + return Status::InvalidArgument( + "Json path error: {} for value: {}", + JsonbErrMsg::getErrMsg(JsonbErrType::E_INVALID_JSON_PATH), + std::string_view(reinterpret_cast<const char*>(path_value.data), + path_value.size)); + } + } + auto jsonb_value = jsonb_data_column->get_data_at(i); + // doc is NOT necessary to be deleted since JsonbDocument will not allocate memory + JsonbDocument* doc = JsonbDocument::createDocument(jsonb_value.data, jsonb_value.size); + JsonbValue* value = doc->getValue()->findValue(path, nullptr); + if (UNLIKELY(jsonb_value.size == 0 || !value)) { + null_map->get_data()[i] = 1; + res->insert_data(nullptr, 0); + continue; + } + auto length = value->length(); + res->insert_data(const_cast<const char*>((char*)&length), 0); + } + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + return Status::OK(); + } +}; + +struct JsonbLengthImpl { + static DataTypes get_variadic_argument_types() { return {std::make_shared<DataTypeJsonb>()}; } + + static Status execute_impl(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + auto path = ColumnString::create(); + std::string root_path = "$"; + + for (int i = 0; i < input_rows_count; i++) { + reinterpret_cast<ColumnString*>(path.get()) + ->insert_data(root_path.data(), root_path.size()); + } + + block.insert({std::move(path), std::make_shared<DataTypeString>(), "path"}); + ColumnNumbers temp_arguments = {arguments[0], block.columns() - 1}; + + return JsonbLengthUtil::jsonb_length_execute(context, block, temp_arguments, result, + input_rows_count); + } +}; + +struct JsonbLengthAndPathImpl { + static DataTypes get_variadic_argument_types() { + return {std::make_shared<DataTypeJsonb>(), std::make_shared<DataTypeString>()}; + } + + static Status execute_impl(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + return JsonbLengthUtil::jsonb_length_execute(context, block, arguments, result, + input_rows_count); + } +}; + +template <typename Impl> +class FunctionJsonbContains : public IFunction { +public: + static constexpr auto name = "json_contains"; + String get_name() const override { return name; } + static FunctionPtr create() { return std::make_shared<FunctionJsonbContains<Impl>>(); } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + return make_nullable(std::make_shared<DataTypeUInt8>()); + } + DataTypes get_variadic_argument_types_impl() const override { + return Impl::get_variadic_argument_types(); + } + size_t get_number_of_arguments() const override { + return get_variadic_argument_types_impl().size(); + } + + bool use_default_implementation_for_nulls() const override { return false; } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) override { + return Impl::execute_impl(context, block, arguments, result, input_rows_count); + } +}; + +struct JsonbContainsUtil { + static Status jsonb_contains_execute(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + DCHECK_GE(arguments.size(), 3); + + auto jsonb_data1_column = block.get_by_position(arguments[0]).column; + auto jsonb_data2_column = block.get_by_position(arguments[1]).column; + + ColumnPtr path_column; + bool is_const = false; + std::tie(path_column, is_const) = + unpack_if_const(block.get_by_position(arguments[2]).column); + + JsonbPath path; + if (is_const) { + auto path_value = path_column->get_data_at(0); + if (!path.seek(path_value.data, path_value.size)) { + return Status::InvalidArgument( + "Json path error: {} for value: {}", + JsonbErrMsg::getErrMsg(JsonbErrType::E_INVALID_JSON_PATH), + std::string_view(reinterpret_cast<const char*>(path_value.data), + path_value.size)); + } + } + auto null_map = ColumnUInt8::create(input_rows_count, 0); + auto return_type = block.get_data_type(result); + MutableColumnPtr res = return_type->create_column(); + + for (size_t i = 0; i < input_rows_count; ++i) { + if (jsonb_data1_column->is_null_at(i) || jsonb_data2_column->is_null_at(i) || + path_column->is_null_at(i)) { + null_map->get_data()[i] = 1; + res->insert_data(nullptr, 0); + continue; + } + + if (!is_const) { + auto path_value = path_column->get_data_at(i); + path.clean(); + if (!path.seek(path_value.data, path_value.size)) { + return Status::InvalidArgument( + "Json path error: {} for value: {}", + JsonbErrMsg::getErrMsg(JsonbErrType::E_INVALID_JSON_PATH), + std::string_view(reinterpret_cast<const char*>(path_value.data), + path_value.size)); + } + } + + auto jsonb_value1 = jsonb_data1_column->get_data_at(i); + auto jsonb_value2 = jsonb_data2_column->get_data_at(i); + + // doc is NOT necessary to be deleted since JsonbDocument will not allocate memory + JsonbDocument* doc1 = + JsonbDocument::createDocument(jsonb_value1.data, jsonb_value1.size); + JsonbDocument* doc2 = + JsonbDocument::createDocument(jsonb_value2.data, jsonb_value2.size); + + JsonbValue* value1 = doc1->getValue()->findValue(path, nullptr); + JsonbValue* value2 = doc2->getValue(); + if (UNLIKELY(jsonb_value1.size == 0 || jsonb_value2.size == 0 || !value1 || !value2)) { + null_map->get_data()[i] = 1; + res->insert_data(nullptr, 0); + continue; + } + auto contains_value = value1->contains(value2); + res->insert_data(const_cast<const char*>((char*)&contains_value), 0); + } + + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); + return Status::OK(); + } +}; + +struct JsonbContainsImpl { + static DataTypes get_variadic_argument_types() { + return {std::make_shared<DataTypeJsonb>(), std::make_shared<DataTypeJsonb>()}; + } + + static Status execute_impl(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + auto path = ColumnString::create(); + std::string root_path = "$"; + + for (int i = 0; i < input_rows_count; i++) { + reinterpret_cast<ColumnString*>(path.get()) + ->insert_data(root_path.data(), root_path.size()); + } + + block.insert({std::move(path), std::make_shared<DataTypeString>(), "path"}); + ColumnNumbers temp_arguments = {arguments[0], arguments[1], block.columns() - 1}; + + return JsonbContainsUtil::jsonb_contains_execute(context, block, temp_arguments, result, + input_rows_count); + } +}; + +struct JsonbContainsAndPathImpl { + static DataTypes get_variadic_argument_types() { + return {std::make_shared<DataTypeJsonb>(), std::make_shared<DataTypeJsonb>(), + std::make_shared<DataTypeString>()}; + } + + static Status execute_impl(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + return JsonbContainsUtil::jsonb_contains_execute(context, block, arguments, result, + input_rows_count); + } +}; + void register_function_jsonb(SimpleFunctionFactory& factory) { factory.register_function<FunctionJsonbParse>(FunctionJsonbParse::name); factory.register_alias(FunctionJsonbParse::name, FunctionJsonbParse::alias); @@ -1067,6 +1320,11 @@ void register_function_jsonb(SimpleFunctionFactory& factory) { factory.register_alias(FunctionJsonbExtractString::name, FunctionJsonbExtractString::alias); factory.register_function<FunctionJsonbExtractJsonb>(); // factory.register_alias(FunctionJsonbExtractJsonb::name, FunctionJsonbExtractJsonb::alias); + + factory.register_function<FunctionJsonbLength<JsonbLengthImpl>>(); + factory.register_function<FunctionJsonbLength<JsonbLengthAndPathImpl>>(); + factory.register_function<FunctionJsonbContains<JsonbContainsImpl>>(); + factory.register_function<FunctionJsonbContains<JsonbContainsAndPathImpl>>(); } } // namespace doris::vectorized diff --git a/docs/en/docs/sql-manual/sql-functions/json-functions/json-contains.md b/docs/en/docs/sql-manual/sql-functions/json-functions/json-contains.md new file mode 100644 index 0000000000..78491b6462 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/json-functions/json-contains.md @@ -0,0 +1,79 @@ +--- +{ +"title": "JSON_CONTAINS", +"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_contains +### description +#### Syntax + +`BOOLEAN json_contains(JSON json_str, JSON candidate)` +`BOOLEAN json_contains(JSON json_str, JSON candidate, VARCHAR json_path)` +`BOOLEAN json_contains(VARCHAR json_str, VARCHAR candidate, VARCHAR json_path)` + + +Indicates by returning 1 or 0 whether a given candidate JSON document is contained at a specific path within the json_str JSON document + +### example + +``` +mysql> SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; +mysql> SET @j2 = '1'; +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.a'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.a') | ++-------------------------------+ +| 1 | ++-------------------------------+ +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.b'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.b') | ++-------------------------------+ +| 0 | ++-------------------------------+ + +mysql> SET @j2 = '{"d": 4}'; +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.a'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.a') | ++-------------------------------+ +| 0 | ++-------------------------------+ +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.c'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.c') | ++-------------------------------+ +| 1 | ++-------------------------------+ + +mysql> SELECT json_contains('[1, 2, {"x": 3}]', '1'); ++----------------------------------------+ +| json_contains('[1, 2, {"x": 3}]', '1') | ++----------------------------------------+ +| 1 | ++----------------------------------------+ +1 row in set (0.04 sec) +``` +### keywords +json,json_contains diff --git a/docs/en/docs/sql-manual/sql-functions/json-functions/json-length.md b/docs/en/docs/sql-manual/sql-functions/json-functions/json-length.md new file mode 100644 index 0000000000..6ea8f25314 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/json-functions/json-length.md @@ -0,0 +1,68 @@ +--- +{ +"title": "JSON_LENGTH", +"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_length +### description +#### Syntax + +`INT json_length(JSON json_str)` +`INT json_length(JSON json_str, VARCHAR json_path)` + +If specified path, the JSON_LENGTH() function returns the length of the data matching the path in the JSON document, otherwise it returns the length of the JSON document. The function calculates the length of the JSON document according to the following rules: + +* The length of a scalar is 1. For example, the length of 1, '"x"', true, false, null is all 1. +* The length of an array is the number of array elements. For example, the length of [1, 2] is 2. +* The length of an object is the number of object members. For example, the length of {"x": 1} is 1. + +### example + +``` +mysql> SELECT json_length('{"k1":"v31","k2":300}'); ++--------------------------------------+ +| json_length('{"k1":"v31","k2":300}') | ++--------------------------------------+ +| 2 | ++--------------------------------------+ +1 row in set (0.26 sec) + +mysql> SELECT json_length('"abc"'); ++----------------------+ +| json_length('"abc"') | ++----------------------+ +| 1 | ++----------------------+ +1 row in set (0.17 sec) + +mysql> SELECT json_length('{"x": 1, "y": [1, 2]}', '$.y'); ++---------------------------------------------+ +| json_length('{"x": 1, "y": [1, 2]}', '$.y') | ++---------------------------------------------+ +| 2 | ++---------------------------------------------+ +1 row in set (0.07 sec) +``` +### keywords +json,json_length diff --git a/docs/sidebars.json b/docs/sidebars.json index 1713bbcbbd..8639eaab51 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -612,6 +612,8 @@ "sql-manual/sql-functions/json-functions/json_quote", "sql-manual/sql-functions/json-functions/json_unquote", "sql-manual/sql-functions/json-functions/json_valid", + "sql-manual/sql-functions/json-functions/json-contains", + "sql-manual/sql-functions/json-functions/json-length", "sql-manual/sql-functions/json-functions/get_json_double", "sql-manual/sql-functions/json-functions/get_json_int", "sql-manual/sql-functions/json-functions/get_json_string" diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-contains.md b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-contains.md new file mode 100644 index 0000000000..31c5403968 --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-contains.md @@ -0,0 +1,79 @@ +--- +{ +"title": "JSON_CONTAINS", +"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_contains +### description +#### Syntax + +`BOOLEAN json_contains(JSON json_str, JSON candidate)` +`BOOLEAN json_contains(JSON json_str, JSON candidate, VARCHAR json_path)` +`BOOLEAN json_contains(VARCHAR json_str, VARCHAR candidate, VARCHAR json_path)` + + +通过返回 1 或 0 来指示给定的 candidate JSON 文档是否包含在 json_str JSON json_path 路径下的文档中 + +### example + +``` +mysql> SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; +mysql> SET @j2 = '1'; +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.a'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.a') | ++-------------------------------+ +| 1 | ++-------------------------------+ +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.b'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.b') | ++-------------------------------+ +| 0 | ++-------------------------------+ + +mysql> SET @j2 = '{"d": 4}'; +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.a'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.a') | ++-------------------------------+ +| 0 | ++-------------------------------+ +mysql> SELECT JSON_CONTAINS(@j, @j2, '$.c'); ++-------------------------------+ +| JSON_CONTAINS(@j, @j2, '$.c') | ++-------------------------------+ +| 1 | ++-------------------------------+ + +mysql> SELECT json_contains('[1, 2, {"x": 3}]', '1'); ++----------------------------------------+ +| json_contains('[1, 2, {"x": 3}]', '1') | ++----------------------------------------+ +| 1 | ++----------------------------------------+ +1 row in set (0.04 sec) +``` +### keywords +json,json_contains diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-length.md b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-length.md new file mode 100644 index 0000000000..5ff9f1c188 --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/json-functions/json-length.md @@ -0,0 +1,68 @@ +--- +{ +"title": "JSON_LENGTH", +"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_length +### description +#### Syntax + +`INT json_length(JSON json_str)` +`INT json_length(JSON json_str, VARCHAR json_path)` + +如果指定path,该JSON_LENGTH()函数返回与 JSON 文档中的路径匹配的数据的长度,否则返回 JSON 文档的长度。该函数根据以下规则计算 JSON 文档的长度: + +* 标量的长度为 1。例如: '1', '"x"', 'true', 'false', 'null' 的长度均为 1。 +* 数组的长度是数组元素的数量。例如: '[1, 2]' 的长度为2。 +* 对象的长度是对象成员的数量。例如: '{"x": 1}' 的长度为1 + +### example + +``` +mysql> SELECT json_length('{"k1":"v31","k2":300}'); ++--------------------------------------+ +| json_length('{"k1":"v31","k2":300}') | ++--------------------------------------+ +| 2 | ++--------------------------------------+ +1 row in set (0.26 sec) + +mysql> SELECT json_length('"abc"'); ++----------------------+ +| json_length('"abc"') | ++----------------------+ +| 1 | ++----------------------+ +1 row in set (0.17 sec) + +mysql> SELECT json_length('{"x": 1, "y": [1, 2]}', '$.y'); ++---------------------------------------------+ +| json_length('{"x": 1, "y": [1, 2]}', '$.y') | ++---------------------------------------------+ +| 2 | ++---------------------------------------------+ +1 row in set (0.07 sec) +``` +### keywords +json,json_length 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 78f4026873..b4d5e0e811 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 @@ -165,7 +165,9 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.If; import org.apache.doris.nereids.trees.expressions.functions.scalar.Initcap; import org.apache.doris.nereids.trees.expressions.functions.scalar.Instr; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonArray; +import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonContains; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtract; +import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonLength; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonObject; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonQuote; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonUnQuote; @@ -532,6 +534,8 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(JsonbParseNullableErrorToNull.class, "jsonb_parse_nullable_error_to_null"), scalar(JsonbParseNullableErrorToValue.class, "jsonb_parse_nullable_error_to_value"), scalar(JsonbType.class, "jsonb_type"), + scalar(JsonLength.class, "json_length"), + scalar(JsonContains.class, "json_conatins"), scalar(LastDay.class, "last_day"), scalar(Least.class, "least"), scalar(Left.class, "left"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonContains.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonContains.java new file mode 100644 index 0000000000..afae87c0a5 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonContains.java @@ -0,0 +1,86 @@ +// 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.literal.Literal; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.JsonType; +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 'json_contains'. This class is generated by GenerateFunction . + */ +public class JsonContains extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, AlwaysNullable { + + public static final List<FunctionSignature> SIGNATURES = ImmutableList.of( + FunctionSignature.ret(BooleanType.INSTANCE).args(JsonType.INSTANCE, JsonType.INSTANCE), + FunctionSignature.ret(BooleanType.INSTANCE).args(JsonType.INSTANCE, JsonType.INSTANCE, + VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(BooleanType.INSTANCE).args(JsonType.INSTANCE, JsonType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 2 arguments. + */ + public JsonContains(Expression arg0, Expression arg1) { + super("json_contains", arg0, arg1, Literal.of("")); + } + + /** + * constructor with 3 arguments. + */ + public JsonContains(Expression arg0, Expression arg1, Expression arg2) { + super("json_contains", arg0, arg1, arg2); + } + + /** + * withChildren. + */ + @Override + public JsonContains withChildren(List<Expression> children) { + Preconditions.checkArgument(children.size() == 2 || children.size() == 3); + if (children.size() == 2) { + return new JsonContains(children.get(0), children.get(1)); + } else { + return new JsonContains(children.get(0), children.get(1), children.get(2)); + } + } + + @Override + public List<FunctionSignature> getSignatures() { + return SIGNATURES; + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitJsonContains(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonLength.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonLength.java new file mode 100644 index 0000000000..fbe127d877 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonLength.java @@ -0,0 +1,86 @@ +// 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.literal.Literal; +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.JsonType; +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 'json_length'. This class is generated by GenerateFunction. + */ +public class JsonLength extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, PropagateNullable { + + public static final List<FunctionSignature> SIGNATURES = ImmutableList.of( + FunctionSignature.ret(IntegerType.INSTANCE).args(JsonType.INSTANCE), + FunctionSignature.ret(IntegerType.INSTANCE).args(JsonType.INSTANCE, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(IntegerType.INSTANCE).args(JsonType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 1 arguments. + */ + public JsonLength(Expression arg0) { + super("json_length", arg0, Literal.of("")); + } + + /** + * constructor with 2 arguments. + */ + public JsonLength(Expression arg0, Expression arg1) { + super("json_length", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public JsonLength withChildren(List<Expression> children) { + Preconditions.checkArgument(children.size() == 1 || children.size() == 2); + if (children.size() == 1) { + return new JsonLength(children.get(0)); + } else { + return new JsonLength(children.get(0), children.get(1)); + } + + } + + @Override + public List<FunctionSignature> getSignatures() { + return SIGNATURES; + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitJsonLength(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 5dcc58fea8..efff54db6e 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 @@ -164,7 +164,9 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.If; import org.apache.doris.nereids.trees.expressions.functions.scalar.Initcap; import org.apache.doris.nereids.trees.expressions.functions.scalar.Instr; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonArray; +import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonContains; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtract; +import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonLength; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonObject; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonQuote; import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonUnQuote; @@ -1062,6 +1064,14 @@ public interface ScalarFunctionVisitor<R, C> { return visitScalarFunction(jsonbType, context); } + default R visitJsonLength(JsonLength jsonLength, C context) { + return visitScalarFunction(jsonLength, context); + } + + default R visitJsonContains(JsonContains jsonContains, C context) { + return visitScalarFunction(jsonContains, context); + } + default R visitLastDay(LastDay lastDay, C context) { return visitScalarFunction(lastDay, context); } diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 0ee7f342cc..14c4c216b2 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -1720,7 +1720,7 @@ visible_functions = { [['json_exists_path'], 'BOOLEAN', ['JSONB', 'VARCHAR'], ''], [['json_exists_path'], 'BOOLEAN', ['JSONB', 'STRING'], ''], - [['json_type'], 'STRING', ['JSONB', 'STRING'], 'ALWAYS_NULLABLE'], + [['json_type'], 'STRING', ['JSONB', 'VARCHAR'], 'ALWAYS_NULLABLE'], [['json_type'], 'STRING', ['JSONB', 'STRING'], 'ALWAYS_NULLABLE'], [['json_extract_isnull'], 'BOOLEAN', ['JSONB', 'VARCHAR'], 'ALWAYS_NULLABLE'], @@ -1737,6 +1737,12 @@ visible_functions = { [['json_extract_double'], 'DOUBLE', ['JSONB', 'STRING'], 'ALWAYS_NULLABLE'], [['json_extract_string'], 'STRING', ['JSONB', 'VARCHAR'], 'ALWAYS_NULLABLE'], [['json_extract_string'], 'STRING', ['JSONB', 'STRING'], 'ALWAYS_NULLABLE'], + [['json_length'], 'INT', ['JSONB'], 'ALWAYS_NULLABLE'], + [['json_length'], 'INT', ['JSONB', 'VARCHAR'], 'ALWAYS_NULLABLE'], + [['json_length'], 'INT', ['JSONB', 'STRING'], 'ALWAYS_NULLABLE'], + [['json_contains'], 'BOOLEAN', ['JSONB', 'JSONB'], 'ALWAYS_NULLABLE'], + [['json_contains'], 'BOOLEAN', ['JSONB', 'JSONB', 'VARCHAR'], 'ALWAYS_NULLABLE'], + [['json_contains'], 'BOOLEAN', ['JSONB', 'JSONB', 'STRING'], 'ALWAYS_NULLABLE'], # Json functions [['get_json_int'], 'INT', ['VARCHAR', 'VARCHAR'], 'ALWAYS_NULLABLE'], @@ -1752,7 +1758,7 @@ visible_functions = { [['json_object'], 'VARCHAR', ['VARCHAR', '...'], 'ALWAYS_NOT_NULLABLE'], [['json_quote'], 'VARCHAR', ['VARCHAR'], ''], [['json_valid'], 'INT', ['VARCHAR'], 'ALWAYS_NULLABLE'], - [['json_contains'], 'INT', ['VARCHAR', 'VARCHAR', 'VARCHAR'], 'ALWAYS_NULLABLE'], + [['json_contains'], 'BOOLEAN', ['VARCHAR', 'VARCHAR', 'VARCHAR'], 'ALWAYS_NULLABLE'], [['json_unquote'], 'VARCHAR', ['VARCHAR'], 'ALWAYS_NULLABLE'], [['json_extract'], 'VARCHAR', ['VARCHAR', 'VARCHAR', '...'], ''] ], diff --git a/regression-test/data/jsonb_p0/test_jsonb_load_and_function.out b/regression-test/data/jsonb_p0/test_jsonb_load_and_function.out index b1731cab21..0e0fabb039 100644 --- a/regression-test/data/jsonb_p0/test_jsonb_load_and_function.out +++ b/regression-test/data/jsonb_p0/test_jsonb_load_and_function.out @@ -7591,3 +7591,265 @@ false 30 -9223372036854775808 null 31 18446744073709551615 null +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +0 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !select_length -- +1 \N \N +2 null 1 +3 true 1 +4 false 1 +5 100 1 +6 10000 1 +7 1000000000 1 +8 1152921504606846976 1 +9 6.18 1 +10 "abcd" 1 +11 {} 0 +12 {"k1":"v31","k2":300} 2 +13 [] 0 +14 [123,456] 2 +15 ["abc","def"] 2 +16 [null,true,false,100,6.18,"abc"] 6 +17 [{"k1":"v41","k2":400},1,"a",3.14] 4 +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} 3 +26 \N \N +27 {"k1":"v1","k2":200} 2 +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} 2 +29 12524337771678448270 1 +30 -9223372036854775808 1 +31 18446744073709551615 1 + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] 1 +15 ["abc","def"] 1 +16 [null,true,false,100,6.18,"abc"] 1 +17 [{"k1":"v41","k2":400},1,"a",3.14] 1 +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} \N +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N +29 12524337771678448270 \N +30 -9223372036854775808 \N +31 18446744073709551615 \N + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} 1 +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} 1 +26 \N \N +27 {"k1":"v1","k2":200} 1 +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N +29 12524337771678448270 \N +30 -9223372036854775808 \N +31 18446744073709551615 \N + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} \N +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N +29 12524337771678448270 \N +30 -9223372036854775808 \N +31 18446744073709551615 \N + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +false + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +false + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +true + +-- !select_json_contains -- +1 \N \N +2 null false +3 true true +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} false +12 {"k1":"v31","k2":300} false +13 [] false +14 [123,456] false +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] true +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} false +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} false +29 12524337771678448270 false +30 -9223372036854775808 false +31 18446744073709551615 false + +-- !select_json_contains -- +1 \N \N +2 null false +3 true false +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} true +12 {"k1":"v31","k2":300} true +13 [] false +14 [123,456] false +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] false +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} true +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} true +29 12524337771678448270 false +30 -9223372036854775808 false +31 18446744073709551615 false + +-- !select_json_contains -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} true +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N +29 12524337771678448270 \N +30 -9223372036854775808 \N +31 18446744073709551615 \N + +-- !select_json_contains -- +1 \N \N +2 null false +3 true false +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} false +12 {"k1":"v31","k2":300} false +13 [] false +14 [123,456] true +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] false +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} false +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} false +29 12524337771678448270 false +30 -9223372036854775808 false +31 18446744073709551615 false + diff --git a/regression-test/data/jsonb_p0/test_jsonb_load_unique_key_and_function.out b/regression-test/data/jsonb_p0/test_jsonb_load_unique_key_and_function.out index 632badf32e..eca0a75867 100644 --- a/regression-test/data/jsonb_p0/test_jsonb_load_unique_key_and_function.out +++ b/regression-test/data/jsonb_p0/test_jsonb_load_unique_key_and_function.out @@ -5867,3 +5867,241 @@ false -- !select -- \N +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +1 + +-- !sql_json_length -- +0 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !sql_json_length -- +2 + +-- !select_length -- +1 \N \N +2 null 1 +3 true 1 +4 false 1 +5 100 1 +6 10000 1 +7 1000000000 1 +8 1152921504606846976 1 +9 6.18 1 +10 "abcd" 1 +11 {} 0 +12 {"k1":"v31","k2":300} 2 +13 [] 0 +14 [123,456] 2 +15 ["abc","def"] 2 +16 [null,true,false,100,6.18,"abc"] 6 +17 [{"k1":"v41","k2":400},1,"a",3.14] 4 +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} 3 +26 \N \N +27 {"k1":"v1","k2":200} 2 +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} 2 + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] 1 +15 ["abc","def"] 1 +16 [null,true,false,100,6.18,"abc"] 1 +17 [{"k1":"v41","k2":400},1,"a",3.14] 1 +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} \N +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} 1 +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} 1 +26 \N \N +27 {"k1":"v1","k2":200} 1 +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N + +-- !select_length -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} \N +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +false + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +false + +-- !sql_json_contains -- +true + +-- !sql_json_contains -- +true + +-- !select_json_contains -- +1 \N \N +2 null false +3 true true +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} false +12 {"k1":"v31","k2":300} false +13 [] false +14 [123,456] false +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] true +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} false +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} false + +-- !select_json_contains -- +1 \N \N +2 null false +3 true false +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} true +12 {"k1":"v31","k2":300} true +13 [] false +14 [123,456] false +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] false +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} true +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} true + +-- !select_json_contains -- +1 \N \N +2 null \N +3 true \N +4 false \N +5 100 \N +6 10000 \N +7 1000000000 \N +8 1152921504606846976 \N +9 6.18 \N +10 "abcd" \N +11 {} \N +12 {"k1":"v31","k2":300} \N +13 [] \N +14 [123,456] \N +15 ["abc","def"] \N +16 [null,true,false,100,6.18,"abc"] \N +17 [{"k1":"v41","k2":400},1,"a",3.14] \N +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} true +26 \N \N +27 {"k1":"v1","k2":200} \N +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} \N + +-- !select_json_contains -- +1 \N \N +2 null false +3 true false +4 false false +5 100 false +6 10000 false +7 1000000000 false +8 1152921504606846976 false +9 6.18 false +10 "abcd" false +11 {} false +12 {"k1":"v31","k2":300} false +13 [] false +14 [123,456] true +15 ["abc","def"] false +16 [null,true,false,100,6.18,"abc"] false +17 [{"k1":"v41","k2":400},1,"a",3.14] false +18 {"k1":"v31","k2":300,"a1":[{"k1":"v41","k2":400},1,"a",3.14]} false +26 \N \N +27 {"k1":"v1","k2":200} false +28 {"a.b.c":{"k1.a1":"v31","k2":300},"a":"niu"} false + 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 e776281b5d..764c02da6f 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 @@ -147,23 +147,23 @@ doris 123 -- !sql -- -1 +true -- !sql -- -0 +false -- !sql -- -0 +false -- !sql -- -1 +true -- !sql -- -0 +false -- !sql -- -1 +true -- !sql -- -1 +true diff --git a/regression-test/suites/jsonb_p0/test_jsonb_load_and_function.groovy b/regression-test/suites/jsonb_p0/test_jsonb_load_and_function.groovy index c885528dbd..821b349d40 100644 --- a/regression-test/suites/jsonb_p0/test_jsonb_load_and_function.groovy +++ b/regression-test/suites/jsonb_p0/test_jsonb_load_and_function.groovy @@ -22,6 +22,8 @@ suite("test_jsonb_load_and_function", "p0") { def testTable = "tbl_test_jsonb" def dataFile = "test_jsonb.csv" + sql """ set experimental_enable_nereids_planner = false """ + sql "DROP TABLE IF EXISTS ${testTable}" sql """ @@ -536,4 +538,36 @@ suite("test_jsonb_load_and_function", "p0") { qt_select """SELECT id, j, j->null FROM ${testTable} ORDER BY id""" qt_select """SELECT id, j, j->'\$.a1[0].k2' FROM ${testTable} ORDER BY id""" qt_select """SELECT id, j, j->'\$.a1[0]'->'\$.k1' FROM ${testTable} ORDER BY id""" + + //json_length + qt_sql_json_length """SELECT json_length('1')""" + qt_sql_json_length """SELECT json_length('true')""" + qt_sql_json_length """SELECT json_length('null')""" + qt_sql_json_length """SELECT json_length('"abc"')""" + qt_sql_json_length """SELECT json_length('[]')""" + qt_sql_json_length """SELECT json_length('[1, 2]')""" + qt_sql_json_length """SELECT json_length('[1, {"x": 2}]')""" + qt_sql_json_length """SELECT json_length('{"x": 1, "y": [1, 2]}', '\$.y')""" + qt_sql_json_length """SELECT json_length('{"k1":"v31","k2":300}')""" + qt_sql_json_length """SELECT json_length('{"a.b.c":{"k1.a1":"v31", "k2": 300},"a":"niu"}')""" + qt_sql_json_length """SELECT json_length('{"a":{"k1.a1":"v31", "k2": 300},"b":"niu"}','\$.a')""" + + qt_select_length """SELECT id, j, json_length(j) FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(j, '\$[1]') FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(j, '\$.k2') FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(null) FROM ${testTable} ORDER BY id""" + + //json_contains + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '1')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '{"x": 3}')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '3')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, [3, 4]]', '2')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, [3, 4]]', '2', '\$[2]')""" + qt_sql_json_contains """SELECT json_contains('{"k1":"v31","k2":300}', '{"k2":300}')""" + qt_sql_json_contains """SELECT json_contains('{"k1":"v31","k2":300}', '{"k2":300,"k1":"v31"}')""" + + qt_select_json_contains """SELECT id, j, json_contains(j, cast('true' as json)) FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('{"k2":300}' as json)) FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('{"k1":"v41","k2":400}' as json), '\$.a1') FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('[123,456]' as json)) FROM ${testTable} ORDER BY id""" } diff --git a/regression-test/suites/jsonb_p0/test_jsonb_load_unique_key_and_function.groovy b/regression-test/suites/jsonb_p0/test_jsonb_load_unique_key_and_function.groovy index 5423ad33fc..3f53721d4b 100644 --- a/regression-test/suites/jsonb_p0/test_jsonb_load_unique_key_and_function.groovy +++ b/regression-test/suites/jsonb_p0/test_jsonb_load_unique_key_and_function.groovy @@ -20,6 +20,8 @@ suite("test_jsonb_unique_load_and_function", "p0") { def testTable = "tbl_test_jsonb_unique" def dataFile = "test_jsonb_unique_key.csv" + sql """ set experimental_enable_nereids_planner = false """ + sql "DROP TABLE IF EXISTS ${testTable}" sql """ @@ -475,4 +477,35 @@ suite("test_jsonb_unique_load_and_function", "p0") { qt_select """SELECT CAST('{x' AS JSONB)""" qt_select """SELECT CAST('[123, abc]' AS JSONB)""" + //json_length + qt_sql_json_length """SELECT json_length('1')""" + qt_sql_json_length """SELECT json_length('true')""" + qt_sql_json_length """SELECT json_length('null')""" + qt_sql_json_length """SELECT json_length('"abc"')""" + qt_sql_json_length """SELECT json_length('[]')""" + qt_sql_json_length """SELECT json_length('[1, 2]')""" + qt_sql_json_length """SELECT json_length('[1, {"x": 2}]')""" + qt_sql_json_length """SELECT json_length('{"x": 1, "y": [1, 2]}', '\$.y')""" + qt_sql_json_length """SELECT json_length('{"k1":"v31","k2":300}')""" + qt_sql_json_length """SELECT json_length('{"a.b.c":{"k1.a1":"v31", "k2": 300},"a":"niu"}')""" + qt_sql_json_length """SELECT json_length('{"a":{"k1.a1":"v31", "k2": 300},"b":"niu"}','\$.a')""" + + qt_select_length """SELECT id, j, json_length(j) FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(j, '\$[1]') FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(j, '\$.k2') FROM ${testTable} ORDER BY id""" + qt_select_length """SELECT id, j, json_length(null) FROM ${testTable} ORDER BY id""" + + //json_contains + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '1')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '{"x": 3}')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, {"x": 3}]', '3')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, [3, 4]]', '2')""" + qt_sql_json_contains """SELECT json_contains('[1, 2, [3, 4]]', '2', '\$[2]')""" + qt_sql_json_contains """SELECT json_contains('{"k1":"v31","k2":300}', '{"k2":300}')""" + qt_sql_json_contains """SELECT json_contains('{"k1":"v31","k2":300}', '{"k2":300,"k1":"v31"}')""" + + qt_select_json_contains """SELECT id, j, json_contains(j, cast('true' as json)) FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('{"k2":300}' as json)) FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('{"k1":"v41","k2":400}' as json), '\$.a1') FROM ${testTable} ORDER BY id""" + qt_select_json_contains """SELECT id, j, json_contains(j, cast('[123,456]' as json)) FROM ${testTable} ORDER BY id""" } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
