mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-13 09:52:38 +00:00
dynamic columns: fix several cases of parsing json
This commit is contained in:
parent
ee7c0d4cc1
commit
3bd96d709d
@ -13,7 +13,6 @@
|
|||||||
#include <Interpreters/convertFieldToType.h>
|
#include <Interpreters/convertFieldToType.h>
|
||||||
#include <Common/HashTable/HashSet.h>
|
#include <Common/HashTable/HashSet.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -28,25 +27,45 @@ namespace ErrorCodes
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Array createEmptyArrayField(size_t num_dimensions)
|
||||||
|
{
|
||||||
|
Array array;
|
||||||
|
Array * current_array = &array;
|
||||||
|
for (size_t i = 1; i < num_dimensions; ++i)
|
||||||
|
{
|
||||||
|
current_array->push_back(Array());
|
||||||
|
current_array = ¤t_array->back().get<Array &>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
class FieldVisitorReplaceNull : public StaticVisitor<Field>
|
class FieldVisitorReplaceNull : public StaticVisitor<Field>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
[[maybe_unused]] explicit FieldVisitorReplaceNull(const Field & replacement_)
|
[[maybe_unused]] explicit FieldVisitorReplaceNull(
|
||||||
|
const Field & replacement_, size_t num_dimensions_)
|
||||||
: replacement(replacement_)
|
: replacement(replacement_)
|
||||||
|
, num_dimensions(num_dimensions_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Field operator()(const Null &) const { return replacement; }
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Field operator()(const T & x) const
|
Field operator()(const T & x) const
|
||||||
{
|
{
|
||||||
if constexpr (std::is_base_of_v<FieldVector, T>)
|
if constexpr (std::is_same_v<T, Null>)
|
||||||
{
|
{
|
||||||
|
return num_dimensions
|
||||||
|
? createEmptyArrayField(num_dimensions)
|
||||||
|
: replacement;
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, Array>)
|
||||||
|
{
|
||||||
|
assert(num_dimensions > 0);
|
||||||
const size_t size = x.size();
|
const size_t size = x.size();
|
||||||
T res(size);
|
Array res(size);
|
||||||
for (size_t i = 0; i < size; ++i)
|
for (size_t i = 0; i < size; ++i)
|
||||||
res[i] = applyVisitor(*this, x[i]);
|
res[i] = applyVisitor(FieldVisitorReplaceNull(replacement, num_dimensions - 1), x[i]);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -54,7 +73,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Field replacement;
|
const Field & replacement;
|
||||||
|
size_t num_dimensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldVisitorToNumberOfDimensions : public StaticVisitor<size_t>
|
class FieldVisitorToNumberOfDimensions : public StaticVisitor<size_t>
|
||||||
@ -66,16 +86,24 @@ public:
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
const size_t size = x.size();
|
const size_t size = x.size();
|
||||||
size_t dimensions = applyVisitor(*this, x[0]);
|
std::optional<size_t> dimensions;
|
||||||
for (size_t i = 1; i < size; ++i)
|
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
|
/// Do not count Nulls, because they will be replaced by default
|
||||||
|
/// values with proper number of dimensions.
|
||||||
|
if (x[i].isNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
size_t current_dimensions = applyVisitor(*this, x[i]);
|
size_t current_dimensions = applyVisitor(*this, x[i]);
|
||||||
if (current_dimensions != dimensions)
|
if (!dimensions)
|
||||||
|
dimensions = current_dimensions;
|
||||||
|
else if (current_dimensions != *dimensions)
|
||||||
throw Exception(ErrorCodes::NUMBER_OF_DIMENSIONS_MISMATHED,
|
throw Exception(ErrorCodes::NUMBER_OF_DIMENSIONS_MISMATHED,
|
||||||
"Number of dimensions mismatched among array elements");
|
"Number of dimensions mismatched among array elements");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1 + dimensions;
|
return 1 + dimensions.value_or(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -239,6 +267,7 @@ void ColumnObject::Subcolumn::insert(Field field)
|
|||||||
if (is_nullable && !base_type->isNullable())
|
if (is_nullable && !base_type->isNullable())
|
||||||
base_type = makeNullable(base_type);
|
base_type = makeNullable(base_type);
|
||||||
|
|
||||||
|
auto value_type = createArrayOfType(base_type, value_dim);
|
||||||
if (!is_nullable && base_type->isNullable())
|
if (!is_nullable && base_type->isNullable())
|
||||||
{
|
{
|
||||||
base_type = removeNullable(base_type);
|
base_type = removeNullable(base_type);
|
||||||
@ -248,10 +277,11 @@ void ColumnObject::Subcolumn::insert(Field field)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
field = applyVisitor(FieldVisitorReplaceNull(base_type->getDefault()), std::move(field));
|
value_type = createArrayOfType(base_type, value_dim);
|
||||||
|
auto default_value = value_type->getDefault();
|
||||||
|
field = applyVisitor(FieldVisitorReplaceNull(default_value, value_dim), std::move(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value_type = createArrayOfType(base_type, value_dim);
|
|
||||||
bool type_changed = false;
|
bool type_changed = false;
|
||||||
|
|
||||||
if (data.empty())
|
if (data.empty())
|
||||||
|
3
tests/queries/0_stateless/01825_type_json_6.reference
Normal file
3
tests/queries/0_stateless/01825_type_json_6.reference
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Tuple(key String, `out.outputs.index` Array(Array(Int32)), `out.outputs.n` Array(Array(Int8)), `out.type` Array(Int8), `out.value` Array(Int8))
|
||||||
|
v1 [0,0] [1,2] [[],[1960131]] [[],[1960131]]
|
||||||
|
v2 [1,1] [4,3] [[1881212],[]] [[1881212],[]]
|
56
tests/queries/0_stateless/01825_type_json_6.sh
Executable file
56
tests/queries/0_stateless/01825_type_json_6.sh
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
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_6;"
|
||||||
|
|
||||||
|
$CLICKHOUSE_CLIENT -q"CREATE TABLE t_json_6 (data JSON) ENGINE = MergeTree ORDER BY tuple();"
|
||||||
|
|
||||||
|
cat <<EOF | $CLICKHOUSE_CLIENT -q "INSERT INTO t_json_6 FORMAT JSONAsObject"
|
||||||
|
{
|
||||||
|
"key": "v1",
|
||||||
|
"out": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": 1,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": 2,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"index": 1960131,
|
||||||
|
"n": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"key": "v2",
|
||||||
|
"out": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": 4,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"index": 1881212,
|
||||||
|
"n": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$CLICKHOUSE_CLIENT -q "SELECT DISTINCT toTypeName(data) FROM t_json_6;"
|
||||||
|
$CLICKHOUSE_CLIENT -q "SELECT data.key, data.out.type, data.out.value, data.out.outputs.index, data.out.outputs.index FROM t_json_6 ORDER BY data.key"
|
||||||
|
|
||||||
|
$CLICKHOUSE_CLIENT -q "DROP TABLE t_json_6;"
|
Loading…
Reference in New Issue
Block a user