From 9b5e702f6c0d4e1aab28a2bed6de830d18a53622 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Wed, 23 Feb 2022 04:28:11 +0300 Subject: [PATCH] fix deducing Array(Nested(...)) and Nested(Array(...)) --- src/DataTypes/ObjectUtils.cpp | 135 ++++++++++-------- src/DataTypes/Serializations/JSONDataParser.h | 4 +- src/DataTypes/Serializations/PathInData.cpp | 58 +++++--- src/DataTypes/Serializations/PathInData.h | 17 ++- .../Serializations/SerializationObject.cpp | 2 +- .../tests/gtest_json_parser.cpp | 76 +++++----- .../queries/0_stateless/01825_type_json_1.sql | 1 - .../0_stateless/01825_type_json_8.reference | 2 + .../queries/0_stateless/01825_type_json_8.sh | 36 +++++ .../01825_type_json_nbagames.reference | 4 +- .../0_stateless/01825_type_json_nbagames.sh | 2 +- 11 files changed, 205 insertions(+), 132 deletions(-) create mode 100644 tests/queries/0_stateless/01825_type_json_8.reference create mode 100644 tests/queries/0_stateless/01825_type_json_8.sh diff --git a/src/DataTypes/ObjectUtils.cpp b/src/DataTypes/ObjectUtils.cpp index 0f09729d009..55ecd073eb6 100644 --- a/src/DataTypes/ObjectUtils.cpp +++ b/src/DataTypes/ObjectUtils.cpp @@ -327,14 +327,9 @@ namespace void flattenTupleImpl( PathInDataBuilder & builder, DataTypePtr type, - size_t array_level, - PathsInData & new_paths, + std::vector & new_paths, DataTypes & new_types) { - bool is_nested = isNested(type); - if (is_nested) - type = assert_cast(*type).getNestedType(); - if (const auto * type_tuple = typeid_cast(type.get())) { const auto & tuple_names = type_tuple->getElementNames(); @@ -342,19 +337,32 @@ void flattenTupleImpl( for (size_t i = 0; i < tuple_names.size(); ++i) { - builder.append(tuple_names[i], is_nested); - flattenTupleImpl(builder, tuple_types[i], array_level + is_nested, new_paths, new_types); + builder.append(tuple_names[i], false); + flattenTupleImpl(builder, tuple_types[i], new_paths, new_types); builder.popBack(); } } else if (const auto * type_array = typeid_cast(type.get())) { - flattenTupleImpl(builder, type_array->getNestedType(), array_level + 1, new_paths, new_types); + PathInDataBuilder element_builder; + std::vector element_paths; + DataTypes element_types; + + flattenTupleImpl(element_builder, type_array->getNestedType(), element_paths, element_types); + assert(element_paths.size() == element_types.size()); + + for (size_t i = 0; i < element_paths.size(); ++i) + { + builder.append(element_paths[i], true); + new_paths.emplace_back(builder.getParts()); + new_types.emplace_back(std::make_shared(element_types[i])); + builder.popBack(element_paths[i].size()); + } } else { new_paths.emplace_back(builder.getParts()); - new_types.push_back(createArrayOfType(type, array_level)); + new_types.emplace_back(type); } } @@ -428,16 +436,16 @@ struct ColumnWithTypeAndDimensions using SubcolumnsTreeWithTypes = SubcolumnsTree; using Node = SubcolumnsTreeWithTypes::Node; -std::pair createTypeFromNode(const Node * node) +ColumnWithTypeAndDimensions createTypeFromNode(const Node * node) { auto collect_tuple_elemets = [](const auto & children) { - std::vector> tuple_elements; + std::vector> tuple_elements; tuple_elements.reserve(children.size()); for (const auto & [name, child] : children) { - auto [column, type] = createTypeFromNode(child.get()); - tuple_elements.emplace_back(name, std::move(column), std::move(type)); + auto column = createTypeFromNode(child.get()); + tuple_elements.emplace_back(name, std::move(column)); } /// Sort to always create the same type for the same set of subcolumns. @@ -446,35 +454,44 @@ std::pair createTypeFromNode(const Node * node) auto tuple_names = extractVector<0>(tuple_elements); auto tuple_columns = extractVector<1>(tuple_elements); - auto tuple_types = extractVector<2>(tuple_elements); - return std::make_tuple(tuple_names, tuple_columns, tuple_types); + return std::make_tuple(std::move(tuple_names), std::move(tuple_columns)); }; if (node->kind == Node::SCALAR) { - return {node->data.column, node->data.type}; + return node->data; } else if (node->kind == Node::NESTED) { + auto [tuple_names, tuple_columns] = collect_tuple_elemets(node->children); + Columns offsets_columns; - ColumnPtr current_column = node->data.column; + offsets_columns.reserve(tuple_columns[0].array_dimensions + 1); - assert(node->data.array_dimensions > 0); - offsets_columns.reserve(node->data.array_dimensions); + const auto & current_array = assert_cast(*node->data.column); + offsets_columns.push_back(current_array.getOffsetsPtr()); - for (size_t i = 0; i < node->data.array_dimensions; ++i) + for (size_t i = 0; i < tuple_columns[0].array_dimensions; ++i) { - const auto & column_array = assert_cast(*current_column); - + const auto & column_array = assert_cast(*tuple_columns[0].column); offsets_columns.push_back(column_array.getOffsetsPtr()); - current_column = column_array.getDataPtr(); + tuple_columns[0].column = column_array.getDataPtr(); } - auto [tuple_names, tuple_columns, tuple_types] = collect_tuple_elemets(node->children); + size_t num_elements = tuple_columns.size(); + Columns tuple_elements_columns(num_elements); + DataTypes tuple_elements_types(num_elements); - auto result_column = ColumnArray::create(ColumnTuple::create(tuple_columns), offsets_columns.back()); - auto result_type = createNested(tuple_types, tuple_names); + for (size_t i = 0; i < num_elements; ++i) + { + assert(tuple_columns[i].array_dimensions == tuple_columns[0].array_dimensions); + tuple_elements_columns[i] = reduceNumberOfDimensions(tuple_columns[i].column, tuple_columns[i].array_dimensions); + tuple_elements_types[i] = reduceNumberOfDimensions(tuple_columns[i].type, tuple_columns[i].array_dimensions); + } + + auto result_column = ColumnArray::create(ColumnTuple::create(tuple_elements_columns), offsets_columns.back()); + auto result_type = createNested(tuple_elements_types, tuple_names); for (auto it = offsets_columns.rbegin() + 1; it != offsets_columns.rend(); ++it) { @@ -482,16 +499,27 @@ std::pair createTypeFromNode(const Node * node) result_type = std::make_shared(result_type); } - return {result_column, result_type}; + return {result_column, result_type, tuple_columns[0].array_dimensions}; } else { - auto [tuple_names, tuple_columns, tuple_types] = collect_tuple_elemets(node->children); + auto [tuple_names, tuple_columns] = collect_tuple_elemets(node->children); - auto result_column = ColumnTuple::create(tuple_columns); - auto result_type = std::make_shared(tuple_types, tuple_names); + size_t num_elements = tuple_columns.size(); + Columns tuple_elements_columns(num_elements); + DataTypes tuple_elements_types(num_elements); - return {result_column, result_type}; + for (size_t i = 0; i < tuple_columns.size(); ++i) + { + assert(tuple_columns[i].array_dimensions == tuple_columns[0].array_dimensions); + tuple_elements_columns[i] = tuple_columns[i].column; + tuple_elements_types[i] = tuple_columns[i].type; + } + + auto result_column = ColumnTuple::create(tuple_elements_columns); + auto result_type = std::make_shared(tuple_elements_types, tuple_names); + + return {result_column, result_type, tuple_columns[0].array_dimensions}; } } @@ -499,11 +527,13 @@ std::pair createTypeFromNode(const Node * node) std::pair flattenTuple(const DataTypePtr & type) { - PathsInData new_paths; + std::vector new_path_parts; DataTypes new_types; PathInDataBuilder builder; - flattenTupleImpl(builder, type, 0, new_paths, new_types); + flattenTupleImpl(builder, type, new_path_parts, new_types); + + PathsInData new_paths(new_path_parts.begin(), new_path_parts.end()); return {new_paths, new_types}; } @@ -546,15 +576,7 @@ std::pair unflattenTuple( auto type = tuple_types[i]; const auto & parts = paths[i].getParts(); - size_t num_parts = parts.size(); - size_t nested_level = std::count_if(parts.begin(), parts.end(), [](const auto & part) { return part.is_nested; }); - size_t array_level = getNumberOfDimensions(*type); - - if (array_level < nested_level) - throw Exception(ErrorCodes::LOGICAL_ERROR, - "Number of dimensions ({}) is less than number Nested types ({}) for path {}", - array_level, nested_level, paths[i].getPath()); size_t pos = 0; tree.add(paths[i], [&](Node::Kind kind, bool exists) -> std::shared_ptr @@ -564,27 +586,13 @@ std::pair unflattenTuple( "Not enough name parts for path {}. Expected at least {}, got {}", paths[i].getPath(), pos + 1, num_parts); - ColumnWithTypeAndDimensions current_column; - if (kind == Node::NESTED) + size_t array_dimensions = kind == Node::NESTED ? 1 : parts[pos].anonymous_array_level; + ColumnWithTypeAndDimensions current_column{column, type, array_dimensions}; + + if (array_dimensions) { - assert(parts[pos].is_nested); - - size_t dimensions_to_reduce = array_level - nested_level + 1; - --nested_level; - - current_column = ColumnWithTypeAndDimensions{column, type, dimensions_to_reduce}; - - if (dimensions_to_reduce) - { - type = reduceNumberOfDimensions(type, dimensions_to_reduce); - column = reduceNumberOfDimensions(column, dimensions_to_reduce); - } - - array_level -= dimensions_to_reduce; - } - else - { - current_column = ColumnWithTypeAndDimensions{column, type, 0}; + type = reduceNumberOfDimensions(type, array_dimensions); + column = reduceNumberOfDimensions(column, array_dimensions); } ++pos; @@ -597,7 +605,8 @@ std::pair unflattenTuple( }); } - return createTypeFromNode(tree.getRoot()); + const auto & [column, type, _] = createTypeFromNode(tree.getRoot()); + return std::make_pair(std::move(column), std::move(type)); } static void addConstantToWithClause(const ASTPtr & query, const String & column_name, const DataTypePtr & data_type) diff --git a/src/DataTypes/Serializations/JSONDataParser.h b/src/DataTypes/Serializations/JSONDataParser.h index 5e45533b225..4a5077f2825 100644 --- a/src/DataTypes/Serializations/JSONDataParser.h +++ b/src/DataTypes/Serializations/JSONDataParser.h @@ -153,13 +153,11 @@ private: paths.reserve(paths.size() + arrays_by_path.size()); values.reserve(values.size() + arrays_by_path.size()); - bool is_nested = arrays_by_path.size() > 1 || !arrays_by_path.begin()->getMapped().first.empty(); - for (auto && [_, value] : arrays_by_path) { auto && [path, path_array] = value; - paths.push_back(builder.append(path, is_nested).getParts()); + paths.push_back(builder.append(path, true).getParts()); values.push_back(std::move(path_array)); builder.popBack(path.size()); diff --git a/src/DataTypes/Serializations/PathInData.cpp b/src/DataTypes/Serializations/PathInData.cpp index 86e8fb9d8a2..cd467b2eefb 100644 --- a/src/DataTypes/Serializations/PathInData.cpp +++ b/src/DataTypes/Serializations/PathInData.cpp @@ -26,13 +26,13 @@ PathInData::PathInData(std::string_view path_) if (*it == '.') { size_t size = static_cast(it - begin); - parts.emplace_back(std::string_view{begin, size}, false); + parts.emplace_back(std::string_view{begin, size}, false, 0); begin = it + 1; } } size_t size = static_cast(end - begin); - parts.emplace_back(std::string_view{begin, size}, false); + parts.emplace_back(std::string_view{begin, size}, false, 0.); } PathInData::PathInData(const Parts & parts_) @@ -65,6 +65,7 @@ UInt128 PathInData::getPartsHash(const Parts & parts_) { hash.update(part.key.data(), part.key.length()); hash.update(part.is_nested); + hash.update(part.anonymous_array_level); } UInt128 res; @@ -78,7 +79,8 @@ void PathInData::writeBinary(WriteBuffer & out) const for (const auto & part : parts) { writeStringBinary(part.key, out); - writeVarUInt(static_cast(part.is_nested) , out); + writeVarUInt(part.is_nested, out); + writeVarUInt(part.anonymous_array_level, out); } } @@ -93,11 +95,14 @@ void PathInData::readBinary(ReadBuffer & in) for (size_t i = 0; i < num_parts; ++i) { - UInt8 is_nested; + bool is_nested; + UInt8 anonymous_array_level; + auto ref = readStringBinaryInto(arena, in); readVarUInt(is_nested, in); + readVarUInt(anonymous_array_level, in); - temp_parts.emplace_back(static_cast(ref), is_nested); + temp_parts.emplace_back(static_cast(ref), is_nested, anonymous_array_level); } path = buildPath(temp_parts); @@ -122,16 +127,16 @@ String PathInData::buildPath(const Parts & other_parts) return res; } -PathInData::Parts PathInData::buildParts(const String & path, const Parts & other_parts) +PathInData::Parts PathInData::buildParts(const String & other_path, const Parts & other_parts) { if (other_parts.empty()) return {}; Parts res; - const char * begin = path.data(); + const char * begin = other_path.data(); for (const auto & part : other_parts) { - res.emplace_back(std::string_view{begin, part.key.length()}, part.is_nested); + res.emplace_back(std::string_view{begin, part.key.length()}, part.is_nested, part.anonymous_array_level); begin += part.key.length() + 1; } return res; @@ -139,24 +144,43 @@ PathInData::Parts PathInData::buildParts(const String & path, const Parts & othe size_t PathInData::Hash::operator()(const PathInData & value) const { - return std::hash{}(value.path); + auto hash = getPartsHash(value.parts); + return hash.items[0] ^ hash.items[1]; } -PathInDataBuilder & PathInDataBuilder::append(std::string_view key, bool is_nested) +PathInDataBuilder & PathInDataBuilder::append(std::string_view key, bool is_array) { - if (!parts.empty()) - parts.back().is_nested = is_nested; + if (parts.empty()) + current_anonymous_array_level += is_array; + + if (!key.empty()) + { + if (!parts.empty()) + parts.back().is_nested = is_array; + + parts.emplace_back(key, false, current_anonymous_array_level); + current_anonymous_array_level = 0; + } - parts.emplace_back(key, false); return *this; } -PathInDataBuilder & PathInDataBuilder::append(const PathInData::Parts & path, bool is_nested) +PathInDataBuilder & PathInDataBuilder::append(const PathInData::Parts & path, bool is_array) { - if (!parts.empty()) - parts.back().is_nested = is_nested; + if (parts.empty()) + current_anonymous_array_level += is_array; + + if (!path.empty()) + { + if (!parts.empty()) + parts.back().is_nested = is_array; + + auto it = parts.insert(parts.end(), path.begin(), path.end()); + for (; it != parts.end(); ++it) + it->anonymous_array_level += current_anonymous_array_level; + current_anonymous_array_level = 0; + } - parts.insert(parts.end(), path.begin(), path.end()); return *this; } diff --git a/src/DataTypes/Serializations/PathInData.h b/src/DataTypes/Serializations/PathInData.h index 04cfff922d0..d4d36e346dc 100644 --- a/src/DataTypes/Serializations/PathInData.h +++ b/src/DataTypes/Serializations/PathInData.h @@ -16,13 +16,16 @@ public: struct Part { Part() = default; - Part(std::string_view key_, bool is_nested_) - : key(key_), is_nested(is_nested_) + Part(std::string_view key_, bool is_nested_, UInt8 anonymous_array_level_) + : key(key_), is_nested(is_nested_), anonymous_array_level(anonymous_array_level_) { } std::string_view key; bool is_nested = false; + UInt8 anonymous_array_level = 0; + + bool operator==(const Part & other) const = default; }; using Parts = std::vector; @@ -47,13 +50,12 @@ public: void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); - bool operator==(const PathInData & other) const { return path == other.path; } - bool operator!=(const PathInData & other) const { return !(*this == other); } + bool operator==(const PathInData & other) const { return parts == other.parts; } struct Hash { size_t operator()(const PathInData & value) const; }; private: static String buildPath(const Parts & other_parts); - static Parts buildParts(const String & path, const Parts & other_parts); + static Parts buildParts(const String & other_path, const Parts & other_parts); String path; Parts parts; @@ -64,14 +66,15 @@ class PathInDataBuilder public: const PathInData::Parts & getParts() const { return parts; } - PathInDataBuilder & append(std::string_view key, bool is_nested); - PathInDataBuilder & append(const PathInData::Parts & path, bool is_nested); + PathInDataBuilder & append(std::string_view key, bool is_array); + PathInDataBuilder & append(const PathInData::Parts & path, bool is_array); void popBack(); void popBack(size_t n); private: PathInData::Parts parts; + size_t current_anonymous_array_level = 0; }; using PathsInData = std::vector; diff --git a/src/DataTypes/Serializations/SerializationObject.cpp b/src/DataTypes/Serializations/SerializationObject.cpp index b306a3f6cbd..b6b4812af57 100644 --- a/src/DataTypes/Serializations/SerializationObject.cpp +++ b/src/DataTypes/Serializations/SerializationObject.cpp @@ -260,7 +260,7 @@ void SerializationObject::serializeBinaryBulkWithMultipleStreams( if (auto * stream = settings.getter(settings.path)) writeVarUInt(column_object.getSubcolumns().size(), *stream); - const auto & subcolumns = column_object.getSubcolumns().getLeaves(); + const auto & subcolumns = column_object.getSubcolumns(); for (const auto & entry : subcolumns) { settings.path.back() = Substream::ObjectStructure; diff --git a/src/DataTypes/Serializations/tests/gtest_json_parser.cpp b/src/DataTypes/Serializations/tests/gtest_json_parser.cpp index b93873b22c6..4dddb3cd03d 100644 --- a/src/DataTypes/Serializations/tests/gtest_json_parser.cpp +++ b/src/DataTypes/Serializations/tests/gtest_json_parser.cpp @@ -1,9 +1,11 @@ #include #include #include -#include #include +#include +#include + #if USE_SIMDJSON using namespace DB; @@ -49,26 +51,32 @@ TEST(JSONDataParser, ReadJSON) struct JSONPathAndValue { - String path; + PathInData path; Field value; - std::vector is_nested; - - JSONPathAndValue(const String & path_, const Field & value_, const std::vector & is_nested_) - : path(path_), value(value_), is_nested(is_nested_) - { - } JSONPathAndValue(const PathInData & path_, const Field & value_) - : path(path_.getPath()), value(value_) + : path(path_), value(value_) { - for (const auto & part : path_.getParts()) - is_nested.push_back(part.is_nested); } bool operator==(const JSONPathAndValue & other) const = default; - bool operator<(const JSONPathAndValue & other) const { return path < other.path; } + bool operator<(const JSONPathAndValue & other) const { return path.getPath() < other.path.getPath(); } }; +static std::ostream & operator<<(std::ostream & ostr, const JSONPathAndValue & path_and_value) +{ + ostr << "{ PathInData{"; + bool first = true; + for (const auto & part : path_and_value.path.getParts()) + { + ostr << (first ? "{" : ", {") << part.key << ", " << part.is_nested << ", " << part.anonymous_array_level << "}"; + first = false; + } + + ostr << "}, Field{" << applyVisitor(FieldVisitorToString(), path_and_value.value) << "} }"; + return ostr; +} + using JSONValues = std::vector; static void check( @@ -100,17 +108,17 @@ TEST(JSONDataParser, Parse) { check(json1, "json1", { - {"k1", 1, {false}}, - {"k2.k3", "aa", {false, false}}, - {"k2.k4", 2, {false, false}}, + { PathInData{{{"k1", false, 0}}}, 1 }, + { PathInData{{{"k2", false, 0}, {"k3", false, 0}}}, "aa" }, + { PathInData{{{"k2", false, 0}, {"k4", false, 0}}}, 2 }, }); } { check(json2, "json2", { - {"k1.k2", Array{"aaa", "ddd"}, {true, false}}, - {"k1.k3.k4", Array{Array{"bbb", "ccc"}, Array{"eee", "fff"}}, {true, true, false}}, + { PathInData{{{"k1", true, 0}, {"k2", false, 0}}}, Array{"aaa", "ddd"} }, + { PathInData{{{"k1", true, 0}, {"k3", true, 0}, {"k4", false, 0}}}, Array{Array{"bbb", "ccc"}, Array{"eee", "fff"}} }, }); } @@ -134,15 +142,11 @@ TEST(JSONDataParser, Parse) } ]})"; - Strings paths = {"k1.k2.k4", "k1.k5", "k1.k2.k3"}; - - auto k1k2k4 = Array{Array{3, 4}, Array{7, 8}}; - check(json3, "json3", { - {"k1.k5", Array{"foo", "bar"}, {true, false}}, - {"k1.k2.k3", Array{Array{1, 2}, Array{5, 6}}, {true, false, false}}, - {"k1.k2.k4", Array{Array{3, 4}, Array{7, 8}}, {true, false, false}}, + { PathInData{{{"k1", true, 0}, {"k5", false, 0}}}, Array{"foo", "bar"} }, + { PathInData{{{"k1", true, 0}, {"k2", false, 0}, {"k3", false, 0}}}, Array{Array{1, 2}, Array{5, 6}} }, + { PathInData{{{"k1", true, 0}, {"k2", false, 0}, {"k4", false, 0}}}, Array{Array{3, 4}, Array{7, 8}} }, }); } @@ -162,15 +166,18 @@ TEST(JSONDataParser, Parse) check(json4, "json4", { - {"k1.k5", Array{"foo", "bar"}, {true, false}}, - {"k1.k2.k3", Array{Array{1, 2}, Array{5, 6}}, {true, true, false}}, - {"k1.k2.k4", Array{Array{3, 4}, Array{7, 8}}, {true, true, false}}, + { PathInData{{{"k1", true, 0}, {"k5", false, 0}}}, Array{"foo", "bar"} }, + { PathInData{{{"k1", true, 0}, {"k2", true, 0}, {"k3", false, 0}}}, Array{Array{1, 2}, Array{5, 6}} }, + { PathInData{{{"k1", true, 0}, {"k2", true, 0}, {"k4", false, 0}}}, Array{Array{3, 4}, Array{7, 8}} }, }); } { const String json5 = R"({"k1": [[1, 2, 3], [4, 5], [6]]})"; - check(json5, "json5", {{"k1", Array{Array{1, 2, 3}, Array{4, 5}, Array{6}}, {false}}}); + check(json5, "json5", + { + { PathInData{{{"k1", false, 0}}}, Array{Array{1, 2, 3}, Array{4, 5}, Array{6}} } + }); } { @@ -182,15 +189,10 @@ TEST(JSONDataParser, Parse) ] })"; - Strings paths = {"k1.k2", "k1.k3"}; - - auto k1k2 = Array{Array{1, 3}, Array{5}}; - auto k1k3 = Array{Array{2, 4}, Array{6}}; - check(json6, "json6", { - {"k1.k2", Array{Array{1, 3}, Array{5}}, {true, false}}, - {"k1.k3", Array{Array{2, 4}, Array{6}}, {true, false}}, + { PathInData{{{"k1", true, 0}, {"k2", false, 1}}}, Array{Array{1, 3}, Array{5}} }, + { PathInData{{{"k1", true, 0}, {"k3", false, 1}}}, Array{Array{2, 4}, Array{6}} }, }); } @@ -205,8 +207,8 @@ TEST(JSONDataParser, Parse) check(json7, "json7", { - {"k1.k2", Array{Array{1, 3}, Array{5}}, {true, false}}, - {"k1.k3", Array{Array{2, 4}, Array{6}}, {true, false}}, + { PathInData{{{"k1", true, 0}, {"k2", false, 0}}}, Array{Array{1, 3}, Array{5}} }, + { PathInData{{{"k1", true, 0}, {"k3", false, 0}}}, Array{Array{2, 4}, Array{6}} }, }); } } diff --git a/tests/queries/0_stateless/01825_type_json_1.sql b/tests/queries/0_stateless/01825_type_json_1.sql index 233d0fae1e8..c2110f35c33 100644 --- a/tests/queries/0_stateless/01825_type_json_1.sql +++ b/tests/queries/0_stateless/01825_type_json_1.sql @@ -26,7 +26,6 @@ FROM system.parts_columns WHERE table = 't_json' AND database = currentDatabase() AND active AND column = 'data' ORDER BY name; - SELECT '============'; TRUNCATE TABLE t_json; diff --git a/tests/queries/0_stateless/01825_type_json_8.reference b/tests/queries/0_stateless/01825_type_json_8.reference new file mode 100644 index 00000000000..b64e6d0c9b9 --- /dev/null +++ b/tests/queries/0_stateless/01825_type_json_8.reference @@ -0,0 +1,2 @@ +([[(1,2),(3,4)],[(5,6)]]) Tuple(k1 Array(Nested(k2 Int8, k3 Int8))) +([([1,3,4,5],[6,7]),([8],[9,10,11])]) Tuple(k1 Nested(k2 Array(Int8), k3 Array(Int8))) diff --git a/tests/queries/0_stateless/01825_type_json_8.sh b/tests/queries/0_stateless/01825_type_json_8.sh new file mode 100644 index 00000000000..e3ae8f23d36 --- /dev/null +++ b/tests/queries/0_stateless/01825_type_json_8.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Tags: no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_CLIENT -q "DROP TABLE IF EXISTS t_json_8" + +$CLICKHOUSE_CLIENT -q "CREATE TABLE t_json_8 (data JSON) ENGINE = MergeTree ORDER BY tuple()" + +cat <