Skip to content
Draft
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
10 changes: 9 additions & 1 deletion be/src/core/block/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ void Block::check_number_of_rows(bool allow_null_columns) const {
}
}

Status Block::check_type_and_column() const {
Status Block::check_type_and_column() {
#ifndef NDEBUG
for (const auto& elem : data) {
if (!elem.column) {
Expand Down Expand Up @@ -336,6 +336,14 @@ Status Block::check_type_and_column() const {
elem.name, column->get_name(), type->get_name(), st.msg());
}
}

for (size_t i = 0; i < data.size(); ++i) {
if (!data[i].column || !data[i].column->is_nullable()) {
continue;
}
auto column_guard = mutate_column_scoped(i);
column_guard.mutable_column()->inject_debug_nullable_payload();
}
#endif
return Status::OK();
}
Expand Down
2 changes: 1 addition & 1 deletion be/src/core/block/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class Block {
/// Checks that every column in block is not nullptr and has same number of elements.
void check_number_of_rows(bool allow_null_columns = false) const;

Status check_type_and_column() const;
Status check_type_and_column();

Status check_column_and_type_not_null() const;

Expand Down
7 changes: 7 additions & 0 deletions be/src/core/column/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,13 @@ class IColumn : public COW<IColumn> {

Status column_self_check() const;

/** Replace physical values selected by null_map with conspicuous debug-only values.
* A top-level call passes nullptr. Wrapper columns forward the applicable null map to their
* nested value column. The caller must own a recursively mutable column tree. Implementations
* must not change column sizes or structural metadata.
*/
virtual void inject_debug_nullable_payload(const uint8_t* null_map = nullptr) {}

// only used in agg value replace for column which is not variable length, eg.BlockReader::_copy_value_data
// usage: self_column.replace_column_data(other_column, other_column's row index, self_column's row index)
virtual void replace_column_data(const IColumn&, size_t row, size_t self_row = 0) = 0;
Expand Down
5 changes: 5 additions & 0 deletions be/src/core/column/column_const.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ class ColumnConst final : public COWHelper<IColumn, ColumnConst> {
bool only_null() const override { return data->is_null_at(0); }
StringRef get_raw_data() const override { return data->get_raw_data(); }

void inject_debug_nullable_payload(const uint8_t* null_map = nullptr) override {
auto mutable_data = data->assert_mutable();
mutable_data->inject_debug_nullable_payload(null_map);
}

/// Not part of the common interface.
const IColumn& get_data_column() const { return *data; }
const ColumnPtr& get_data_column_ptr() const { return data; }
Expand Down
11 changes: 11 additions & 0 deletions be/src/core/column/column_decimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ class ColumnDecimal final : public COWHelper<IColumn, ColumnDecimal<T>> {
memset(data.data() + old_size, 0, length * sizeof(data[0]));
}

void inject_debug_nullable_payload(const uint8_t* null_map = nullptr) override {
if (null_map == nullptr) {
return;
}
for (size_t i = 0; i < data.size(); ++i) {
if (null_map[i] != 0) {
memset(&data[i], i % 2 == 0 ? 0x80 : 0x7F, sizeof(value_type));
}
}
}

Status filter_by_selector(const uint16_t* sel, size_t sel_size,
IColumn* col_ptr) const override {
Self* output = assert_cast<Self*>(col_ptr);
Expand Down
5 changes: 5 additions & 0 deletions be/src/core/column/column_nullable.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ class ColumnNullable final : public COWHelper<IColumn, ColumnNullable> {
bool is_nullable() const override { return true; }
bool is_column_string() const override { return get_nested_column().is_column_string(); }

void inject_debug_nullable_payload(const uint8_t* null_map = nullptr) override {
DCHECK(null_map == nullptr);
get_nested_column().inject_debug_nullable_payload(get_null_map_data().data());
}

bool is_exclusive() const override {
return IColumn::is_exclusive() && _nested_column->is_exclusive() &&
get_null_map_column().is_exclusive();
Expand Down
32 changes: 32 additions & 0 deletions be/src/core/column/column_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <limits>
#include <string>
#include <type_traits>
#include <typeinfo>
Expand Down Expand Up @@ -186,6 +187,37 @@ class ColumnVector final : public COWHelper<IColumn, ColumnVector<T>> {
std::fill(data.data() + old_size, data.data() + old_size + length, default_value());
}

void inject_debug_nullable_payload(const uint8_t* null_map = nullptr) override {
if (null_map == nullptr) {
return;
}
for (size_t i = 0; i < data.size(); ++i) {
if (null_map[i] == 0) {
continue;
}
if constexpr (T == TYPE_BOOLEAN) {
data[i] = static_cast<value_type>(0xA5);
} else if constexpr (is_int(T)) {
data[i] = i % 2 == 0 ? std::numeric_limits<value_type>::lowest()
: std::numeric_limits<value_type>::max();
} else if constexpr (is_float_or_double(T) || T == TYPE_TIMEV2) {
switch (i % 3) {
case 0:
data[i] = std::numeric_limits<value_type>::quiet_NaN();
break;
case 1:
data[i] = std::numeric_limits<value_type>::infinity();
break;
default:
data[i] = -std::numeric_limits<value_type>::infinity();
break;
}
} else {
memset(&data[i], 0xFF, sizeof(value_type));
}
}
}

void pop_back(size_t n) override { data.resize_assume_reserved(data.size() - n); }

StringRef serialize_value_into_arena(size_t n, Arena& arena, char const*& begin) const override;
Expand Down
63 changes: 63 additions & 0 deletions be/test/core/data_type/block_check_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

#include <gtest/gtest.h>

#include <cmath>
#include <limits>

#include "core/block/block.h"
#include "core/column/column_const.h"
#include "core/column/column_nullable.h"
#include "core/column/column_string.h"
#include "core/column/column_vector.h"
Expand Down Expand Up @@ -79,4 +83,63 @@ TEST(BlockCheckType, CheckNoColumnString64) {
st = block.check_no_column_string64();
EXPECT_FALSE(st.ok());
}

#ifndef NDEBUG
TEST(BlockCheckType, InjectDebugNullablePayload) {
auto original = ColumnHelper::create_nullable_column<DataTypeInt32>({1, 2, 3, 4}, {1, 1, 0, 0});
Block block {{original, make_nullable(std::make_shared<DataTypeInt32>()), "nullable_int"}};

ASSERT_TRUE(block.check_type_and_column().ok());

const auto& nullable = assert_cast<const ColumnNullable&>(*block.get_by_position(0).column);
const auto& values = assert_cast<const ColumnInt32&>(nullable.get_nested_column()).get_data();
EXPECT_EQ(values[0], std::numeric_limits<int32_t>::lowest());
EXPECT_EQ(values[1], std::numeric_limits<int32_t>::max());
EXPECT_EQ(values[2], 3);
EXPECT_EQ(values[3], 4);

const auto& original_nullable = assert_cast<const ColumnNullable&>(*original);
const auto& original_values =
assert_cast<const ColumnInt32&>(original_nullable.get_nested_column()).get_data();
EXPECT_EQ(original_values[0], 1);
EXPECT_EQ(original_values[1], 2);
}

TEST(BlockCheckType, InjectDebugNullableBooleanAndFloatPayload) {
Block block {{ColumnHelper::create_nullable_column<DataTypeUInt8>({0, 1}, {1, 0}),
make_nullable(std::make_shared<DataTypeUInt8>()), "nullable_bool"},
{ColumnHelper::create_nullable_column<DataTypeFloat64>({0.0, 1.0}, {1, 0}),
make_nullable(std::make_shared<DataTypeFloat64>()), "nullable_double"}};

ASSERT_TRUE(block.check_type_and_column().ok());

const auto& nullable_bool =
assert_cast<const ColumnNullable&>(*block.get_by_position(0).column);
const auto& bool_values =
assert_cast<const ColumnUInt8&>(nullable_bool.get_nested_column()).get_data();
EXPECT_EQ(bool_values[0], 0xA5);
EXPECT_EQ(bool_values[1], 1);

const auto& nullable_double =
assert_cast<const ColumnNullable&>(*block.get_by_position(1).column);
const auto& double_values =
assert_cast<const ColumnFloat64&>(nullable_double.get_nested_column()).get_data();
EXPECT_TRUE(std::isnan(double_values[0]));
EXPECT_EQ(double_values[1], 1.0);
}

TEST(BlockCheckType, InjectDebugNullablePayloadThroughConstColumn) {
auto nullable = ColumnHelper::create_nullable_column<DataTypeInt32>({0}, {1});
Block block {{ColumnConst::create(std::move(nullable), 3),
make_nullable(std::make_shared<DataTypeInt32>()), "const_null"}};

ASSERT_TRUE(block.check_type_and_column().ok());

const auto& column_const = assert_cast<const ColumnConst&>(*block.get_by_position(0).column);
const auto& nullable_data = assert_cast<const ColumnNullable&>(column_const.get_data_column());
const auto& values =
assert_cast<const ColumnInt32&>(nullable_data.get_nested_column()).get_data();
EXPECT_EQ(values[0], std::numeric_limits<int32_t>::lowest());
}
#endif
} // namespace doris
Loading