Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix columnObject crash with array of variadic dimension elems #40483

Merged
merged 4 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Columns/ColumnObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,15 @@ FieldInfo getFieldInfo(const Field & field)
{
FieldVisitorToScalarType to_scalar_type_visitor;
applyVisitor(to_scalar_type_visitor, field);
FieldVisitorToNumberOfDimensions to_number_dimension_visitor;

return
{
to_scalar_type_visitor.getScalarType(),
to_scalar_type_visitor.haveNulls(),
to_scalar_type_visitor.needConvertField(),
applyVisitor(FieldVisitorToNumberOfDimensions(), field),
applyVisitor(to_number_dimension_visitor, field),
to_number_dimension_visitor.need_fold_dimension
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/Columns/ColumnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ struct FieldInfo

/// Number of dimension in array. 0 if field is scalar.
size_t num_dimensions;

/// If true then this field is an array of variadic dimension field
/// and we need to normalize the dimension
bool need_fold_dimension;
};

FieldInfo getFieldInfo(const Field & field);
Expand Down
21 changes: 19 additions & 2 deletions src/DataTypes/ObjectUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,14 +737,31 @@ Field FieldVisitorReplaceScalars::operator()(const Array & x) const
return res;
}

size_t FieldVisitorToNumberOfDimensions::operator()(const Array & x) const
size_t FieldVisitorToNumberOfDimensions::operator()(const Array & x)
{
const size_t size = x.size();
size_t dimensions = 0;
for (size_t i = 0; i < size; ++i)
dimensions = std::max(dimensions, applyVisitor(*this, x[i]));
{
size_t element_dimensions = applyVisitor(*this, x[i]);
if (i > 0 && element_dimensions != dimensions)
need_fold_dimension = true;
dimensions = std::max(dimensions, element_dimensions);
}

return 1 + dimensions;
}

Field FieldVisitorFoldDimension::operator()(const Array & x) const
{
if (num_dimensions_to_fold == 0)
return x;
const size_t size = x.size();
Array res(size);
for (size_t i = 0; i < size; ++i)
{
res[i] = applyVisitor(FieldVisitorFoldDimension(num_dimensions_to_fold - 1), x[i]);
}
return res;
}
}
34 changes: 33 additions & 1 deletion src/DataTypes/ObjectUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,42 @@ class FieldVisitorReplaceScalars : public StaticVisitor<Field>
class FieldVisitorToNumberOfDimensions : public StaticVisitor<size_t>
{
public:
size_t operator()(const Array & x) const;
size_t operator()(const Array & x);

template <typename T>
size_t operator()(const T &) const { return 0; }

bool need_fold_dimension = false;
};

/// Fold field (except Null) to the higher dimension, e.g. `1` -- fold 2 --> `[[1]]`
/// used to normalize dimension of element in an array. e.g [1, [2]] --> [[1], [2]]
class FieldVisitorFoldDimension : public StaticVisitor<Field>
{
public:
explicit FieldVisitorFoldDimension(size_t num_dimensions_to_fold_) : num_dimensions_to_fold(num_dimensions_to_fold_) { }

Field operator()(const Array & x) const;

Field operator()(const Null & x) const { return x; }

template <typename T>
Field operator()(const T & x) const
{
if (num_dimensions_to_fold == 0)
return x;
Array res(1,x);
for (size_t i = 1; i < num_dimensions_to_fold; ++i)
{
Array new_res;
new_res.push_back(std::move(res));
res = std::move(new_res);
}
return res;
}

private:
size_t num_dimensions_to_fold;
};

/// Receives range of objects, which contains collections
Expand Down
2 changes: 2 additions & 0 deletions src/DataTypes/Serializations/SerializationObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ void SerializationObject<Parser>::deserializeTextImpl(IColumn & column, Reader &
for (size_t i = 0; i < paths.size(); ++i)
{
auto field_info = getFieldInfo(values[i]);
if (field_info.need_fold_dimension)
values[i] = applyVisitor(FieldVisitorFoldDimension(field_info.num_dimensions), std::move(values[i]));
if (isNothing(field_info.scalar_type))
continue;

Expand Down
4 changes: 4 additions & 0 deletions tests/queries/0_stateless/02287_type_object_convert.reference
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
2 (0,2) Tuple(x Int8, y Int8)
{"x":1}
{"x":1}
{"x":[[1],[1,2]]}
{"x":[[],[1,2]]}
{"x":[[],[[1],[2]]]}
{"x":[[],[[],[2]]]}
8 changes: 6 additions & 2 deletions tests/queries/0_stateless/02287_type_object_convert.sql
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ SELECT id, data, toTypeName(data) FROM t_object_convert2 ORDER BY id;
DROP TABLE t_object_convert;
DROP TABLE t_object_convert2;

select CAST(CAST('{"x" : 1}', 'Object(\'json\')'), 'Object(Nullable(\'json\'))');
select CAST(CAST('{"x" : 1}', 'Object(Nullable(\'json\'))'), 'Object(\'json\')');
SELECT CAST(CAST('{"x" : 1}', 'Object(\'json\')'), 'Object(Nullable(\'json\'))');
SELECT CAST(CAST('{"x" : 1}', 'Object(Nullable(\'json\'))'), 'Object(\'json\')');
SELECT CAST('{"x" : [ 1 , [ 1 , 2] ]}', 'Object(\'json\')');
SELECT CAST('{"x" : [ {} , [ 1 , 2] ]}', 'Object(\'json\')');
SELECT CAST('{"x" : [ {} , [ 1 , [2]] ]}', 'Object(\'json\')');
SELECT CAST('{"x" : [ {} , [ {} , [2]] ]}', 'Object(\'json\')');