This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 470d4e97cdc [refine](column) enforce nullable nested types for array
(#63088) (#64698)
470d4e97cdc is described below
commit 470d4e97cdcebafcd5a91bfce795748424bf161f
Author: Mryange <[email protected]>
AuthorDate: Tue Jun 23 10:16:32 2026 +0800
[refine](column) enforce nullable nested types for array (#63088) (#64698)
This PR makes the nested types inside Array explicitly nullable in BE
type implementations, instead of relying on implicit caller-side
conventions.
DataTypeArray now always stores nullable nested element type
DataTypeArraySerDe updated to follow the same invariant
https://github.com/apache/doris/pull/63088
### What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [ ] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
---
be/src/core/data_type/data_type_array.cpp | 17 +++--
be/src/core/data_type/data_type_array.h | 11 ++--
be/src/core/data_type/primitive_type.h | 2 +
.../core/data_type_serde/data_type_array_serde.cpp | 4 +-
.../core/data_type_serde/data_type_array_serde.h | 10 ++-
.../data_type_serde/data_type_nullable_serde.h | 3 +
be/src/exprs/function/cast/cast_to_array.h | 2 +-
be/test/core/block/block_test.cpp | 6 +-
.../data_type_serde_get_name_test.cpp | 4 +-
.../data_type_serde_string_test.cpp | 8 ++-
be/test/core/jsonb/serialize_test.cpp | 6 +-
.../data/vec/native/all_types_single_row.native | Bin 1135 -> 1140 bytes
be/test/exec/common/schema_util_test.cpp | 4 +-
.../exec/operator/table_function_operator_test.cpp | 55 ++++++++--------
be/test/exprs/aggregate/agg_array_agg_test.cpp | 70 ---------------------
be/test/exprs/aggregate/agg_replace_test.cpp | 6 +-
be/test/format/orc/orc_reader_fill_data_test.cpp | 48 +++++++-------
.../java/org/apache/doris/catalog/ArrayType.java | 57 +++++++++++------
.../doris/nereids/parser/LogicalPlanBuilder.java | 2 +-
.../expression/rules/FoldConstantRuleOnBE.java | 4 +-
.../nereids/rules/rewrite/NestedColumnPruning.java | 5 +-
.../trees/expressions/ArrayItemReference.java | 3 +-
.../functions/AggCombinerFunctionBuilder.java | 3 +-
.../functions/ComputeSignatureHelper.java | 2 +-
.../functions/combinator/ForEachCombinator.java | 2 +-
.../expressions/functions/scalar/ArrayMap.java | 2 +-
.../expressions/functions/scalar/ArraySort.java | 4 +-
.../org/apache/doris/nereids/types/ArrayType.java | 28 ++++-----
.../org/apache/doris/nereids/types/DataType.java | 4 +-
.../doris/nereids/util/TypeCoercionUtils.java | 3 +-
.../java/org/apache/doris/catalog/TypeTest.java | 6 +-
.../nereids/trees/expressions/LiteralTest.java | 12 ++--
32 files changed, 187 insertions(+), 206 deletions(-)
diff --git a/be/src/core/data_type/data_type_array.cpp
b/be/src/core/data_type/data_type_array.cpp
index f1841f0e2dc..5a9d2064fa9 100644
--- a/be/src/core/data_type/data_type_array.cpp
+++ b/be/src/core/data_type/data_type_array.cpp
@@ -48,7 +48,13 @@ namespace ErrorCodes {
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
}
-DataTypeArray::DataTypeArray(const DataTypePtr& nested_) : nested {nested_} {}
+DataTypeArray::DataTypeArray(const DataTypePtr& nested_) {
+ DataTypePtr nullable_nested = make_nullable(nested_);
+ auto nested_type = std::dynamic_pointer_cast<const
DataTypeNullable>(nullable_nested);
+ DORIS_CHECK(nested_type != nullptr);
+ nested = std::move(nested_type);
+ nested_as_base = nested;
+}
MutableColumnPtr DataTypeArray::create_column() const {
return ColumnArray::create(nested->create_column(),
ColumnArray::ColumnOffsets::create());
@@ -66,9 +72,10 @@ bool DataTypeArray::equals(const IDataType& rhs) const {
// here we should remove nullable, otherwise here always be 1
size_t DataTypeArray::get_number_of_dimensions() const {
- const DataTypeArray* nested_array =
- typeid_cast<const DataTypeArray*>(remove_nullable(nested).get());
- if (!nested_array) return 1;
+ auto* nested_array = typeid_cast<const
DataTypeArray*>(remove_nullable(nested).get());
+ if (!nested_array) {
+ return 1;
+ }
return 1 +
nested_array
->get_number_of_dimensions(); /// Every modern C++ compiler
optimizes tail recursion.
@@ -166,7 +173,7 @@ const char* DataTypeArray::deserialize(const char* buf,
MutableColumnPtr* column
void DataTypeArray::to_pb_column_meta(PColumnMeta* col_meta) const {
IDataType::to_pb_column_meta(col_meta);
- auto children = col_meta->add_children();
+ auto* children = col_meta->add_children();
get_nested_type()->to_pb_column_meta(children);
}
diff --git a/be/src/core/data_type/data_type_array.h
b/be/src/core/data_type/data_type_array.h
index f72e2c18537..26479859121 100644
--- a/be/src/core/data_type/data_type_array.h
+++ b/be/src/core/data_type/data_type_array.h
@@ -29,6 +29,7 @@
#include "common/status.h"
#include "core/data_type/data_type.h"
+#include "core/data_type/data_type_nullable.h"
#include "core/data_type/define_primitive_type.h"
#include "core/data_type_serde/data_type_array_serde.h"
#include "core/data_type_serde/data_type_serde.h"
@@ -47,7 +48,8 @@ namespace doris {
class DataTypeArray final : public IDataType {
private:
/// The type of array elements.
- DataTypePtr nested;
+ DataTypeNullablePtr nested;
+ DataTypePtr nested_as_base;
public:
static constexpr PrimitiveType PType = TYPE_ARRAY;
@@ -74,7 +76,8 @@ public:
bool equals(const IDataType& rhs) const override;
- const DataTypePtr& get_nested_type() const { return nested; }
+ const DataTypePtr& get_nested_type() const { return nested_as_base; }
+ const DataTypeNullablePtr& get_nullable_nested_type() const { return
nested; }
/// 1 for plain array, 2 for array of arrays and so on.
size_t get_number_of_dimensions() const;
@@ -94,7 +97,7 @@ public:
void to_protobuf(PTypeDesc* ptype, PTypeNode* node, PScalarType*
scalar_type) const override {
node->set_type(TTypeNodeType::ARRAY);
node->set_contains_null(nested->is_nullable());
- nested->to_protobuf(ptype);
+ get_nested_type()->to_protobuf(ptype);
}
#ifdef BE_TEST
@@ -102,7 +105,7 @@ public:
node.type = TTypeNodeType::ARRAY;
node.__isset.contains_nulls = true;
node.contains_nulls.push_back(nested->is_nullable());
- nested->to_thrift(thrift_type);
+ get_nested_type()->to_thrift(thrift_type);
}
#endif
};
diff --git a/be/src/core/data_type/primitive_type.h
b/be/src/core/data_type/primitive_type.h
index 3ecc6590fab..86b81b5a28e 100644
--- a/be/src/core/data_type/primitive_type.h
+++ b/be/src/core/data_type/primitive_type.h
@@ -93,10 +93,12 @@ class DataTypeHLL;
class DataTypeJsonb;
class DataTypeArray;
class DataTypeMap;
+class DataTypeNullable;
class DataTypeVariant;
class DataTypeStruct;
class DataTypeBitMap;
class DataTypeQuantileState;
+using DataTypeNullablePtr = std::shared_ptr<const DataTypeNullable>;
template <PrimitiveType T>
class ColumnVector;
using ColumnUInt8 = ColumnVector<TYPE_BOOLEAN>;
diff --git a/be/src/core/data_type_serde/data_type_array_serde.cpp
b/be/src/core/data_type_serde/data_type_array_serde.cpp
index 9a78fc1f6d3..c7b139083df 100644
--- a/be/src/core/data_type_serde/data_type_array_serde.cpp
+++ b/be/src/core/data_type_serde/data_type_array_serde.cpp
@@ -86,7 +86,7 @@ Status
DataTypeArraySerDe::deserialize_one_cell_from_json(IColumn& column, Slice
auto& array_column = assert_cast<ColumnArray&>(column);
auto& offsets = array_column.get_offsets();
IColumn& nested_column = array_column.get_data();
- DCHECK(nested_column.is_nullable());
+ DORIS_CHECK(nested_column.is_nullable());
if (slice[0] != '[') {
return Status::InvalidArgument("Array does not start with '['
character, found '{}'",
slice[0]);
@@ -164,7 +164,7 @@ Status
DataTypeArraySerDe::deserialize_one_cell_from_hive_text(
auto& array_column = assert_cast<ColumnArray&>(column);
auto& offsets = array_column.get_offsets();
IColumn& nested_column = array_column.get_data();
- DCHECK(nested_column.is_nullable());
+ DORIS_CHECK(nested_column.is_nullable());
char collection_delimiter =
options.get_collection_delimiter(hive_text_complex_type_delimiter_level);
diff --git a/be/src/core/data_type_serde/data_type_array_serde.h
b/be/src/core/data_type_serde/data_type_array_serde.h
index a7a37f324d2..ceb97b7d57f 100644
--- a/be/src/core/data_type_serde/data_type_array_serde.h
+++ b/be/src/core/data_type_serde/data_type_array_serde.h
@@ -25,6 +25,7 @@
#include <utility>
#include "common/status.h"
+#include "core/data_type_serde/data_type_nullable_serde.h"
#include "core/data_type_serde/data_type_serde.h"
namespace doris {
@@ -38,7 +39,12 @@ class IDataType;
class DataTypeArraySerDe : public DataTypeSerDe {
public:
DataTypeArraySerDe(DataTypeSerDeSPtr _nested_serde, int nesting_level = 1)
- : DataTypeSerDe(nesting_level),
nested_serde(std::move(_nested_serde)) {}
+ : DataTypeSerDe(nesting_level) {
+ auto nullable_serde =
+
std::dynamic_pointer_cast<DataTypeNullableSerDe>(std::move(_nested_serde));
+ DORIS_CHECK(nullable_serde != nullptr);
+ nested_serde = std::move(nullable_serde);
+ }
std::string get_name() const override { return "Array(" +
nested_serde->get_name() + ")"; }
@@ -132,6 +138,6 @@ private:
template <bool is_strict_mode>
Status _from_string(StringRef& str, IColumn& column, const FormatOptions&
options) const;
- DataTypeSerDeSPtr nested_serde;
+ DataTypeNullableSerDeSPtr nested_serde;
};
} // namespace doris
diff --git a/be/src/core/data_type_serde/data_type_nullable_serde.h
b/be/src/core/data_type_serde/data_type_nullable_serde.h
index 4363e4a573b..31ad0e0c041 100644
--- a/be/src/core/data_type_serde/data_type_nullable_serde.h
+++ b/be/src/core/data_type_serde/data_type_nullable_serde.h
@@ -130,5 +130,8 @@ public:
private:
DataTypeSerDeSPtr nested_serde;
};
+
+using DataTypeNullableSerDeSPtr = std::shared_ptr<DataTypeNullableSerDe>;
+
#include "common/compile_check_end.h"
} // namespace doris
diff --git a/be/src/exprs/function/cast/cast_to_array.h
b/be/src/exprs/function/cast/cast_to_array.h
index add20080e66..cf3413ba94a 100644
--- a/be/src/exprs/function/cast/cast_to_array.h
+++ b/be/src/exprs/function/cast/cast_to_array.h
@@ -52,7 +52,7 @@ WrapperType create_array_wrapper(FunctionContext* context,
const DataTypePtr& fr
"CAST AS Array can only be performed between same-dimensional
array types");
}
- const DataTypePtr& to_nested_type = to_type.get_nested_type();
+ DataTypePtr to_nested_type = to_type.get_nested_type();
/// Prepare nested type conversion
const auto nested_function =
diff --git a/be/test/core/block/block_test.cpp
b/be/test/core/block/block_test.cpp
index 40a6d2ee99e..89f019ff06f 100644
--- a/be/test/core/block/block_test.cpp
+++ b/be/test/core/block/block_test.cpp
@@ -99,7 +99,8 @@ static void fill_block_with_array_int(Block& block) {
data_column->insert_data((const char*)(&v), 0);
}
- auto column_array_ptr = ColumnArray::create(std::move(data_column),
std::move(off_column));
+ auto column_array_ptr =
+ ColumnArray::create(make_nullable(std::move(data_column)),
std::move(off_column));
DataTypePtr nested_type(std::make_shared<DataTypeInt32>());
DataTypePtr array_type(std::make_shared<DataTypeArray>(nested_type));
ColumnWithTypeAndName test_array_int(std::move(column_array_ptr),
array_type, "test_array_int");
@@ -119,7 +120,8 @@ static void fill_block_with_array_string(Block& block) {
data_column->insert_data(v.data(), v.size());
}
- auto column_array_ptr = ColumnArray::create(std::move(data_column),
std::move(off_column));
+ auto column_array_ptr =
+ ColumnArray::create(make_nullable(std::move(data_column)),
std::move(off_column));
DataTypePtr nested_type(std::make_shared<DataTypeString>());
DataTypePtr array_type(std::make_shared<DataTypeArray>(nested_type));
ColumnWithTypeAndName test_array_string(std::move(column_array_ptr),
array_type,
diff --git a/be/test/core/data_type_serde/data_type_serde_get_name_test.cpp
b/be/test/core/data_type_serde/data_type_serde_get_name_test.cpp
index 11212b2bd09..bbeaf81ce87 100644
--- a/be/test/core/data_type_serde/data_type_serde_get_name_test.cpp
+++ b/be/test/core/data_type_serde/data_type_serde_get_name_test.cpp
@@ -45,7 +45,7 @@ TEST(DataTypeSerDeGetNameTest, test) {
{
auto type =
std::make_shared<DataTypeArray>(std::make_shared<DataTypeInt64>());
auto serde = type->get_serde();
- EXPECT_EQ(serde->get_name(), "Array(BIGINT)");
+ EXPECT_EQ(serde->get_name(), "Array(Nullable(BIGINT))");
}
{
@@ -114,7 +114,7 @@ TEST(DataTypeSerDeGetNameTest, test) {
auto serde = type->get_serde();
EXPECT_EQ(
serde->get_name(),
- R"(Struct(field1:String, field2:BIGINT, field3:DOUBLE,
field4:Array(INT), field5:Map(String, BIGINT), field6:Nullable(String),
field7:Nullable(BIGINT)))");
+ R"(Struct(field1:String, field2:BIGINT, field3:DOUBLE,
field4:Array(Nullable(INT)), field5:Map(String, BIGINT),
field6:Nullable(String), field7:Nullable(BIGINT)))");
}
}
diff --git a/be/test/core/data_type_serde/data_type_serde_string_test.cpp
b/be/test/core/data_type_serde/data_type_serde_string_test.cpp
index c7bb93a77d1..3708145e391 100644
--- a/be/test/core/data_type_serde/data_type_serde_string_test.cpp
+++ b/be/test/core/data_type_serde/data_type_serde_string_test.cpp
@@ -32,6 +32,7 @@
#include "core/assert_cast.h"
#include "core/column/column.h"
#include "core/column/column_array.h"
+#include "core/column/column_nullable.h"
#include "core/data_type/common_data_type_serder_test.h"
#include "core/data_type/common_data_type_test.h"
#include "core/data_type/data_type.h"
@@ -308,9 +309,12 @@ TEST_F(DataTypeStringSerDeTest,
ArrowMemNotAlignedNestedArr) {
EXPECT_EQ(values_address % 4, 1);
// 5.Test read_column_from_arrow
- auto ser_col = ColumnArray::create(ColumnString::create(),
ColumnOffset64::create());
+ auto ser_col = ColumnArray::create(
+ ColumnNullable::create(ColumnString::create(),
ColumnUInt8::create()),
+ ColumnOffset64::create());
cctz::time_zone tz;
- auto serde_list = std::make_shared<DataTypeArraySerDe>(serde_str);
+ auto serde_nullable_str =
std::make_shared<DataTypeNullableSerDe>(serde_str);
+ auto serde_list = std::make_shared<DataTypeArraySerDe>(serde_nullable_str);
auto st = serde_list->read_column_from_arrow(*ser_col, arr.get(), 0, 1,
tz);
EXPECT_TRUE(st.ok());
}
diff --git a/be/test/core/jsonb/serialize_test.cpp
b/be/test/core/jsonb/serialize_test.cpp
index d576523548e..2419383b0ed 100644
--- a/be/test/core/jsonb/serialize_test.cpp
+++ b/be/test/core/jsonb/serialize_test.cpp
@@ -591,7 +591,8 @@ static void fill_block_with_array_int(Block& block) {
data_column->insert_data((const char*)(&v), 0);
}
- auto column_array_ptr = ColumnArray::create(std::move(data_column),
std::move(off_column));
+ auto column_array_ptr =
+ ColumnArray::create(make_nullable(std::move(data_column)),
std::move(off_column));
DataTypePtr nested_type(std::make_shared<DataTypeInt32>());
DataTypePtr array_type(std::make_shared<DataTypeArray>(nested_type));
ColumnWithTypeAndName test_array_int(std::move(column_array_ptr),
array_type, "test_array_int");
@@ -611,7 +612,8 @@ static void fill_block_with_array_string(Block& block) {
data_column->insert_data(v.data(), v.size());
}
- auto column_array_ptr = ColumnArray::create(std::move(data_column),
std::move(off_column));
+ auto column_array_ptr =
+ ColumnArray::create(make_nullable(std::move(data_column)),
std::move(off_column));
DataTypePtr nested_type(std::make_shared<DataTypeString>());
DataTypePtr array_type(std::make_shared<DataTypeArray>(nested_type));
ColumnWithTypeAndName test_array_string(std::move(column_array_ptr),
array_type,
diff --git a/be/test/data/vec/native/all_types_single_row.native
b/be/test/data/vec/native/all_types_single_row.native
index 742d1b7b30c..fe56a0a778b 100644
Binary files a/be/test/data/vec/native/all_types_single_row.native and
b/be/test/data/vec/native/all_types_single_row.native differ
diff --git a/be/test/exec/common/schema_util_test.cpp
b/be/test/exec/common/schema_util_test.cpp
index 2350bed1b76..c663cbc1a23 100644
--- a/be/test/exec/common/schema_util_test.cpp
+++ b/be/test/exec/common/schema_util_test.cpp
@@ -889,8 +889,8 @@ TEST_F(SchemaUtilTest, TestCastColumnWithExecuteFailure) {
auto simple_type = std::make_shared<DataTypeJsonb>();
// Insert some test dataset
- auto nested_array =
- ColumnArray::create(ColumnIPv4::create(),
ColumnArray::ColumnOffsets::create());
+ auto nested_array =
ColumnArray::create(make_nullable(ColumnIPv4::create()),
+
ColumnArray::ColumnOffsets::create());
nested_array->insert(Field::create_field<PrimitiveType::TYPE_ARRAY>(Array(IPv4(1))));
nested_array->insert(Field::create_field<PrimitiveType::TYPE_ARRAY>(Array(IPv4(2))));
diff --git a/be/test/exec/operator/table_function_operator_test.cpp
b/be/test/exec/operator/table_function_operator_test.cpp
index bd7b108b18a..24079a18e75 100644
--- a/be/test/exec/operator/table_function_operator_test.cpp
+++ b/be/test/exec/operator/table_function_operator_test.cpp
@@ -42,6 +42,11 @@
namespace doris {
+static MutableColumnPtr create_nullable_nested_array_column(MutableColumnPtr
nested,
+ MutableColumnPtr
offsets) {
+ return ColumnArray::create(make_nullable(std::move(nested)),
std::move(offsets));
+}
+
class MockTableFunctionChildOperator : public OperatorXBase {
public:
Status get_block_after_projects(RuntimeState* state, Block* block, bool*
eos) override {
@@ -372,7 +377,7 @@ TEST_F(TableFunctionOperatorTest, block_fast_path_explode) {
offsets->insert_value(3);
offsets->insert_value(3);
offsets->insert_value(4);
- auto arr_col = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_col = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
push_child_block(
Block({ColumnWithTypeAndName(id_col, int_type, "id"),
@@ -398,8 +403,8 @@ TEST_F(TableFunctionOperatorTest, block_fast_path_explode) {
expected_offsets->insert_value(3);
expected_offsets->insert_value(5);
expected_offsets->insert_value(6);
- auto expected_arr =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_out = ColumnHelper::create_column<DataTypeInt32>({1, 2,
3, 4});
@@ -431,7 +436,7 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_batch_truncate) {
offsets->insert_value(3);
offsets->insert_value(3);
offsets->insert_value(4);
- auto arr_col = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_col = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
push_child_block(
Block({ColumnWithTypeAndName(id_col, int_type, "id"),
@@ -451,8 +456,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_batch_truncate) {
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(1);
expected_offsets->insert_value(3);
- auto expected_arr =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_out = ColumnHelper::create_column<DataTypeInt32>({1, 2});
auto int_type = std::make_shared<DataTypeInt32>();
@@ -475,8 +480,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_batch_truncate) {
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(2);
expected_offsets->insert_value(3);
- auto expected_arr =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_out = ColumnHelper::create_column<DataTypeInt32>({3, 4});
auto int_type = std::make_shared<DataTypeInt32>();
@@ -503,7 +508,7 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_skip) {
offsets->insert_value(1);
offsets->insert_value(1);
offsets->insert_value(2);
- auto arr_col = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_col = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
push_child_block(
Block({ColumnWithTypeAndName(id_col, int_type, "id"),
@@ -523,8 +528,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_skip) {
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(1);
expected_offsets->insert_value(2);
- auto expected_arr =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_out = ColumnHelper::create_column<DataTypeInt32>({1, 2});
@@ -552,7 +557,7 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_null_ro
offsets->insert_value(1);
offsets->insert_value(1);
offsets->insert_value(2);
- auto arr_data = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_data = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
auto null_map = ColumnUInt8::create();
null_map->insert_value(0);
null_map->insert_value(1);
@@ -576,8 +581,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_null_ro
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(1);
expected_offsets->insert_value(2);
- auto expected_arr_data =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr_data =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_arr_null = ColumnUInt8::create();
expected_arr_null->insert_value(0);
expected_arr_null->insert_value(0);
@@ -611,7 +616,7 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_misalig
offsets->insert_value(1);
offsets->insert_value(2);
offsets->insert_value(3);
- auto arr_data = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_data = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
auto null_map = ColumnUInt8::create();
null_map->insert_value(0);
null_map->insert_value(1);
@@ -635,8 +640,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_array_misalig
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(1);
expected_offsets->insert_value(2);
- auto expected_arr_data =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr_data =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_arr_null = ColumnUInt8::create();
expected_arr_null->insert_value(0);
expected_arr_null->insert_value(0);
@@ -674,7 +679,7 @@ TEST_F(TableFunctionOperatorTest,
offsets->insert_value(3);
offsets->insert_value(4);
offsets->insert_value(5);
- auto arr_data = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_data = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
auto null_map = ColumnUInt8::create();
null_map->insert_value(0);
null_map->insert_value(1);
@@ -702,8 +707,8 @@ TEST_F(TableFunctionOperatorTest,
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(3);
expected_offsets->insert_value(6);
- auto expected_arr_data =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr_data =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_arr_null = ColumnUInt8::create();
expected_arr_null->insert_value(0);
expected_arr_null->insert_value(0);
@@ -730,8 +735,8 @@ TEST_F(TableFunctionOperatorTest,
auto expected_offsets = ColumnArray::ColumnOffsets::create();
expected_offsets->insert_value(3);
expected_offsets->insert_value(4);
- auto expected_arr_data =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr_data =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_arr_null = ColumnUInt8::create();
expected_arr_null->insert_value(0);
expected_arr_null->insert_value(0);
@@ -768,7 +773,7 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_elements) {
auto offsets = ColumnArray::ColumnOffsets::create();
offsets->insert_value(3);
- auto arr_col = ColumnArray::create(std::move(nested),
std::move(offsets));
+ auto arr_col = create_nullable_nested_array_column(std::move(nested),
std::move(offsets));
push_child_block(
Block({ColumnWithTypeAndName(id_col, int_type, "id"),
@@ -808,8 +813,8 @@ TEST_F(TableFunctionOperatorTest,
block_fast_path_explode_nullable_elements) {
expected_offsets->insert_value(3);
expected_offsets->insert_value(6);
expected_offsets->insert_value(9);
- auto expected_arr =
- ColumnArray::create(std::move(expected_nested),
std::move(expected_offsets));
+ auto expected_arr =
create_nullable_nested_array_column(std::move(expected_nested),
+
std::move(expected_offsets));
auto expected_out_data = ColumnInt32::create();
expected_out_data->insert_value(1);
diff --git a/be/test/exprs/aggregate/agg_array_agg_test.cpp
b/be/test/exprs/aggregate/agg_array_agg_test.cpp
index 101328496df..97b6d99456b 100644
--- a/be/test/exprs/aggregate/agg_array_agg_test.cpp
+++ b/be/test/exprs/aggregate/agg_array_agg_test.cpp
@@ -122,74 +122,4 @@ TEST_F(AggregateFunctionArrayAggTest,
test_array_agg_astr_nullable) {
ColumnWithTypeAndName(std::move(array_column), array_data_type,
"column"));
}
-TEST_F(AggregateFunctionArrayAggTest, test_array_agg_astr_foreach) {
- auto data_type = make_nullable(std::make_shared<DataTypeString>());
- auto array_data_type = std::make_shared<DataTypeArray>(data_type);
- create_agg("array_agg_foreach", false, {array_data_type}, array_data_type);
-
- auto off_column = ColumnOffset64::create();
- auto data_column = data_type->create_column();
- std::vector<ColumnArray::Offset64> offs = {0, 4};
- std::vector<int64_t> vals = {1, 2, 3};
- for (size_t i = 1; i < offs.size(); ++i) {
- off_column->insert_data((const char*)(&offs[i]), 0);
- }
- data_column->insert_default();
- for (auto& v : vals) {
- data_column->insert_data((const char*)(&v), sizeof(v));
- }
- auto array_column = ColumnArray::create(data_column->clone(),
off_column->clone());
-
- auto off_column2 = ColumnOffset64::create();
- std::vector<ColumnArray::Offset64> offs2 = {0, 1, 2, 3, 4};
- for (size_t i = 1; i < offs2.size(); ++i) {
- off_column2->insert_data((const char*)(&offs2[i]), 0);
- }
-
- auto array_array_data_type =
std::make_shared<DataTypeArray>(array_data_type);
- auto array_array_off_column = ColumnOffset64::create();
- array_array_off_column->insert_value(4);
- auto array_array_column =
- ColumnArray::create(ColumnArray::create(data_column->clone(),
off_column2->clone()),
- array_array_off_column->clone());
-
- execute(Block({ColumnWithTypeAndName(array_column->clone(),
array_data_type, "")}),
- ColumnWithTypeAndName(std::move(array_array_column),
array_array_data_type, "column"));
-}
-
-TEST_F(AggregateFunctionArrayAggTest, test_array_agg_aint64_foreach) {
- auto data_type = make_nullable(std::make_shared<DataTypeInt64>());
- auto array_data_type = std::make_shared<DataTypeArray>(data_type);
- create_agg("array_agg_foreach", false, {array_data_type}, array_data_type);
-
- auto off_column = ColumnOffset64::create();
- auto data_column = data_type->create_column();
- std::vector<ColumnArray::Offset64> offs = {0, 4};
- std::vector<int64_t> vals = {1, 2, 3};
- for (size_t i = 1; i < offs.size(); ++i) {
- off_column->insert_data((const char*)(&offs[i]), 0);
- }
- data_column->insert_default();
- for (auto& v : vals) {
- data_column->insert_data((const char*)(&v), sizeof(v));
- }
- auto array_column = ColumnArray::create(data_column->clone(),
off_column->clone());
-
- auto off_column2 = ColumnOffset64::create();
- std::vector<ColumnArray::Offset64> offs2 = {0, 1, 2, 3, 4};
- for (size_t i = 1; i < offs2.size(); ++i) {
- off_column2->insert_data((const char*)(&offs2[i]), 0);
- }
-
- auto array_array_data_type =
std::make_shared<DataTypeArray>(array_data_type);
- auto array_array_off_column = ColumnOffset64::create();
- array_array_off_column->insert_value(4);
- auto array_array_column =
- ColumnArray::create(ColumnArray::create(data_column->clone(),
off_column2->clone()),
- array_array_off_column->clone());
-
- execute(Block({ColumnWithTypeAndName(array_column->clone(),
array_data_type, "")}),
- ColumnWithTypeAndName(std::move(array_array_column),
array_array_data_type, "column"));
-}
-
} // namespace doris
diff --git a/be/test/exprs/aggregate/agg_replace_test.cpp
b/be/test/exprs/aggregate/agg_replace_test.cpp
index a0e90ede1cf..bb00051cbee 100644
--- a/be/test/exprs/aggregate/agg_replace_test.cpp
+++ b/be/test/exprs/aggregate/agg_replace_test.cpp
@@ -123,7 +123,11 @@ public:
auto* data_col = array_col->get_data_ptr().get();
EXPECT_EQ(data_col->size(), expect_num);
for (size_t i = 0; i < expect_num; ++i) {
- check_column_basic<DataType, nullable>(data_col, i, i);
+ if (data_col->is_nullable()) {
+ check_column_basic<DataType, true>(data_col, i, i);
+ } else {
+ check_column_basic<DataType, false>(data_col, i, i);
+ }
}
}
diff --git a/be/test/format/orc/orc_reader_fill_data_test.cpp
b/be/test/format/orc/orc_reader_fill_data_test.cpp
index 12c1dd209c5..eab2b97e38a 100644
--- a/be/test/format/orc/orc_reader_fill_data_test.cpp
+++ b/be/test/format/orc/orc_reader_fill_data_test.cpp
@@ -187,30 +187,30 @@ TEST_F(OrcReaderFillDataTest, ComplexTypeConversionTest) {
std::cout << block.dump_data() << "\n";
ASSERT_EQ(block.dump_data(),
- "+---------------------------+\n"
- "|cc(Struct(col1:Array(INT)))|\n"
- "+---------------------------+\n"
- "| {\"col1\":[0]}|\n"
- "| {\"col1\":[1, 1]}|\n"
- "| {\"col1\":[2, 2, 2]}|\n"
- "| {\"col1\":[3, 3, 3, 3]}|\n"
- "| {\"col1\":[4, 4, 4, 4, 4]}|\n"
- "| {\"col1\":[5]}|\n"
- "| {\"col1\":[6, 6]}|\n"
- "| {\"col1\":[7, 7, 7]}|\n"
- "| {\"col1\":[8, 8, 8, 8]}|\n"
- "| {\"col1\":[9, 9, 9, 9, 9]}|\n"
- "| {\"col1\":[10]}|\n"
- "| {\"col1\":[11, 11]}|\n"
- "| {\"col1\":[12, 12, 12]}|\n"
- "| {\"col1\":[13, 13, 13, 13]}|\n"
- "|{\"col1\":[14, 14, 14, 14,...|\n"
- "| {\"col1\":[15]}|\n"
- "| {\"col1\":[16, 16]}|\n"
- "| {\"col1\":[17, 17, 17]}|\n"
- "| {\"col1\":[18, 18, 18, 18]}|\n"
- "|{\"col1\":[19, 19, 19, 19,...|\n"
- "+---------------------------+\n");
+ "+-------------------------------------+\n"
+ "|cc(Struct(col1:Array(Nullable(INT))))|\n"
+ "+-------------------------------------+\n"
+ "| {\"col1\":[0]}|\n"
+ "| {\"col1\":[1, 1]}|\n"
+ "| {\"col1\":[2, 2, 2]}|\n"
+ "| {\"col1\":[3, 3, 3, 3]}|\n"
+ "| {\"col1\":[4, 4, 4, 4, 4]}|\n"
+ "| {\"col1\":[5]}|\n"
+ "| {\"col1\":[6, 6]}|\n"
+ "| {\"col1\":[7, 7, 7]}|\n"
+ "| {\"col1\":[8, 8, 8, 8]}|\n"
+ "| {\"col1\":[9, 9, 9, 9, 9]}|\n"
+ "| {\"col1\":[10]}|\n"
+ "| {\"col1\":[11, 11]}|\n"
+ "| {\"col1\":[12, 12, 12]}|\n"
+ "| {\"col1\":[13, 13, 13, 13]}|\n"
+ "| {\"col1\":[14, 14, 14, 14, 14]}|\n"
+ "| {\"col1\":[15]}|\n"
+ "| {\"col1\":[16, 16]}|\n"
+ "| {\"col1\":[17, 17, 17]}|\n"
+ "| {\"col1\":[18, 18, 18, 18]}|\n"
+ "| {\"col1\":[19, 19, 19, 19, 19]}|\n"
+ "+-------------------------------------+\n");
}
{
diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java
b/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java
index fb53269d630..3c66f6dd24a 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java
@@ -31,6 +31,11 @@ import java.util.Objects;
/**
* Describes an ARRAY type.
+ *
+ * <p>Array elements are always nullable in Doris. The {@code containsNull}
field is retained
+ * only for backward compatibility with serialized metadata (Gson). It is
always treated as
+ * {@code true} at runtime. The two-argument constructor is kept but
deprecated; prefer the
+ * single-argument constructor {@link #ArrayType(Type)} which defaults to
nullable elements.</p>
*/
public class ArrayType extends Type {
@@ -39,6 +44,8 @@ public class ArrayType extends Type {
@SerializedName(value = "itemType")
private final Type itemType;
+ // Retained for Gson deserialization compatibility with older metadata.
+ // Always treated as true at runtime — array elements are always nullable.
@SerializedName(value = "containsNull")
private final boolean containsNull;
@@ -48,20 +55,31 @@ public class ArrayType extends Type {
}
public ArrayType(Type itemType) {
- this(itemType, true);
+ this.itemType = itemType;
+ this.containsNull = true;
}
+ /**
+ * @deprecated Array elements are always nullable. Use {@link
#ArrayType(Type)} instead.
+ * This constructor is retained only for call-site
compatibility during transition.
+ */
+ @Deprecated
public ArrayType(Type itemType, boolean containsNull) {
this.itemType = itemType;
- this.containsNull = containsNull;
+ // Ignore the parameter — always true
+ this.containsNull = true;
}
public Type getItemType() {
return itemType;
}
+ /**
+ * Always returns {@code true}. Array elements are always nullable in
Doris.
+ */
public boolean getContainsNull() {
- return containsNull;
+ // Always true — array elements are always nullable
+ return true;
}
@Override
@@ -83,10 +101,7 @@ public class ArrayType extends Type {
return false;
}
- if (((ArrayType) t).getContainsNull() != getContainsNull()) {
- return false;
- }
-
+ // containsNull is always true, no need to compare
return itemType.matchesType(((ArrayType) t).itemType);
}
@@ -94,24 +109,27 @@ public class ArrayType extends Type {
return new ArrayType();
}
+ public static ArrayType create(Type type) {
+ return new ArrayType(type);
+ }
+
+ /**
+ * @deprecated Array elements are always nullable. Use {@link
#create(Type)} instead.
+ */
+ @Deprecated
public static ArrayType create(Type type, boolean containsNull) {
- return new ArrayType(type, containsNull);
+ return new ArrayType(type);
}
@Override
public String toSql(int depth) {
- StringBuilder typeStr = new StringBuilder();
- typeStr.append("array<").append(itemType.toSql(depth + 1));
- if (!containsNull) {
- typeStr.append(" not null");
- }
- typeStr.append(">");
- return typeStr.toString();
+ // Array elements are always nullable, no "not null" suffix needed
+ return "array<" + itemType.toSql(depth + 1) + ">";
}
@Override
public int hashCode() {
- return Objects.hash(itemType, containsNull);
+ return Objects.hash(itemType);
}
@Override
@@ -120,7 +138,7 @@ public class ArrayType extends Type {
return false;
}
ArrayType otherArrayType = (ArrayType) other;
- return otherArrayType.itemType.equals(itemType) &&
otherArrayType.containsNull == containsNull;
+ return otherArrayType.itemType.equals(itemType);
}
@Override
@@ -129,8 +147,9 @@ public class ArrayType extends Type {
container.types.add(node);
Preconditions.checkNotNull(itemType);
node.setType(TTypeNodeType.ARRAY);
- node.setContainsNull(containsNull);
- node.setContainsNulls(Lists.newArrayList(containsNull));
+ // Array elements are always nullable
+ node.setContainsNull(true);
+ node.setContainsNulls(Lists.newArrayList(true));
itemType.toThrift(container);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 53035261347..a157819e341 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -5239,7 +5239,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
return ParserUtils.withOrigin(ctx, () -> {
switch (ctx.complex.getType()) {
case DorisParser.ARRAY:
- return ArrayType.of(typedVisit(ctx.dataType(0)), true);
+ return ArrayType.of(typedVisit(ctx.dataType(0)));
case DorisParser.MAP:
return MapType.of(typedVisit(ctx.dataType(0)),
typedVisit(ctx.dataType(1)));
case DorisParser.STRUCT:
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
index 4568e641646..331d70aac8e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
@@ -628,13 +628,13 @@ public class FoldConstantRuleOnBE implements
ExpressionPatternRuleFactory {
private static Pair<DataType, Integer>
convertToNereidsType(List<PTypeNode> typeNodes, int start) {
PScalarType pScalarType = typeNodes.get(start).getScalarType();
- boolean containsNull = typeNodes.get(start).getContainsNull();
TPrimitiveType tPrimitiveType =
TPrimitiveType.findByValue(pScalarType.getType());
DataType type;
int parsedNodes;
if (tPrimitiveType == TPrimitiveType.ARRAY) {
Pair<DataType, Integer> itemType = convertToNereidsType(typeNodes,
start + 1);
- type = ArrayType.of(itemType.key(), containsNull);
+ // Array elements are always nullable
+ type = ArrayType.of(itemType.key());
parsedNodes = 1 + itemType.value();
} else if (tPrimitiveType == TPrimitiveType.MAP) {
Pair<DataType, Integer> keyType = convertToNereidsType(typeNodes,
start + 1);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
index 4bd08510741..22344210a1a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
@@ -344,8 +344,7 @@ public class NestedColumnPruning implements CustomRewriter {
children.values().iterator().next().pruneCastType(
origin.children.values().iterator().next(),
cast.children.values().iterator().next()
- ),
- ((ArrayType) cast.type).containsNull()
+ )
);
} else if (type instanceof MapType) {
return MapType.of(
@@ -535,7 +534,7 @@ public class NestedColumnPruning implements CustomRewriter {
}
return new StructType(newFields);
} else if (dataType instanceof ArrayType) {
- return ArrayType.of(newChildrenTypes.get(0).second,
((ArrayType) dataType).containsNull());
+ return ArrayType.of(newChildrenTypes.get(0).second);
} else if (dataType instanceof MapType) {
return MapType.of(newChildrenTypes.get(0).second,
newChildrenTypes.get(1).second);
} else {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java
index b763d6741ab..bfbb184ec30 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java
@@ -77,7 +77,8 @@ public class ArrayItemReference extends NamedExpression
implements ExpectsInputT
@Override
public boolean nullable() {
- return ((ArrayType)
(this.children.get(0).getDataType())).containsNull();
+ // Array elements are always nullable
+ return true;
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
index bf7ba61039d..436e8aafabd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java
@@ -95,7 +95,8 @@ public class AggCombinerFunctionBuilder extends
FunctionBuilder {
"foreach must be input array type: '" + nestedName);
}
DataType itemType = ((ArrayType) arrayType).getItemType();
- return new SlotReference("mocked", itemType, (((ArrayType)
arrayType).containsNull()));
+ // Array elements are always nullable
+ return new SlotReference("mocked", itemType, true);
}).collect(Collectors.toList());
return (AggregateFunction) nestedBuilder.build(nestedName,
forEachargs).first;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
index c8629fb7b84..0d0abe92789 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
@@ -483,7 +483,7 @@ public class ComputeSignatureHelper {
return signature;
}
ArrayType arrayType = (ArrayType) signature.returnType;
- return signature.withReturnType(ArrayType.of(arrayType.getItemType(),
true));
+ return signature.withReturnType(ArrayType.of(arrayType.getItemType()));
}
// for time type with precision(now are DateTimeV2Type and TimeV2Type),
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/combinator/ForEachCombinator.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/combinator/ForEachCombinator.java
index be52d59b62c..3c5fce06159 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/combinator/ForEachCombinator.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/combinator/ForEachCombinator.java
@@ -119,7 +119,7 @@ public class ForEachCombinator extends
NullableAggregateFunction
@Override
public DataType getDataType() {
- return ArrayType.of(nested.getDataType(), true);
+ return ArrayType.of(nested.getDataType());
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
index 1a1a246cea6..1ef0e996d0d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMap.java
@@ -68,7 +68,7 @@ public class ArrayMap extends ScalarFunction
public DataType getDataType() {
Preconditions.checkArgument(children.get(0) instanceof Lambda,
"The first arg of array_map must be lambda");
- return ArrayType.of(((Lambda) children.get(0)).getRetType(), true);
+ return ArrayType.of(((Lambda) children.get(0)).getRetType());
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java
index 983d3038152..3a905016da0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java
@@ -90,11 +90,11 @@ public class ArraySort extends ScalarFunction
ArrayItemReference argRef = lambda.getLambdaArguments().get(0);
Expression arrayExpr = argRef.getArrayExpression();
ArrayType arrayType = (ArrayType) arrayExpr.getDataType();
- return ArrayType.of(arrayType.getItemType(), true);
+ return ArrayType.of(arrayType.getItemType());
} else if (children.get(0).getDataType() instanceof ArrayType) {
Expression arrayExpr = children.get(0);
ArrayType arrayType = (ArrayType) arrayExpr.getDataType();
- return ArrayType.of(arrayType.getItemType(), true);
+ return ArrayType.of(arrayType.getItemType());
} else {
throw new AnalysisException("The first arg of array_sort must be
lambda or array");
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/ArrayType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/ArrayType.java
index c189154fb42..015690e6434 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/ArrayType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/ArrayType.java
@@ -25,45 +25,39 @@ import java.util.Objects;
/**
* Array type in Nereids.
+ *
+ * <p>Note: Array elements are always nullable in Doris. The NOT NULL
constraint on array elements
+ * (e.g., ARRAY<INT NOT NULL>) is not supported. This simplification
aligns with the actual
+ * behavior where both FE planning and BE execution always treat array
elements as nullable.</p>
*/
public class ArrayType extends DataType implements ComplexDataType,
NestedColumnPrunable {
- public static final ArrayType SYSTEM_DEFAULT = new
ArrayType(NullType.INSTANCE, true);
+ public static final ArrayType SYSTEM_DEFAULT = new
ArrayType(NullType.INSTANCE);
public static final int WIDTH = 64;
private final DataType itemType;
- private final boolean containsNull;
- private ArrayType(DataType itemType, boolean containsNull) {
+ private ArrayType(DataType itemType) {
this.itemType = Objects.requireNonNull(itemType, "itemType can not be
null");
- this.containsNull = containsNull;
}
public static ArrayType of(DataType itemType) {
- return of(itemType, true);
- }
-
- public static ArrayType of(DataType itemType, boolean containsNull) {
if (itemType.equals(NullType.INSTANCE)) {
return SYSTEM_DEFAULT;
}
- return new ArrayType(itemType, containsNull);
+ return new ArrayType(itemType);
}
@Override
public DataType conversion() {
- return new ArrayType(itemType.conversion(), containsNull);
+ return new ArrayType(itemType.conversion());
}
public DataType getItemType() {
return itemType;
}
- public boolean containsNull() {
- return containsNull;
- }
-
@Override
public boolean isInjectiveCastTo(DataType target) {
if (target instanceof ArrayType) {
@@ -74,7 +68,8 @@ public class ArrayType extends DataType implements
ComplexDataType, NestedColumn
@Override
public Type toCatalogDataType() {
- return new
org.apache.doris.catalog.ArrayType(itemType.toCatalogDataType(), containsNull);
+ // Catalog ArrayType defaults containsNull to true via single-arg
constructor
+ return new
org.apache.doris.catalog.ArrayType(itemType.toCatalogDataType());
}
@Override
@@ -94,8 +89,7 @@ public class ArrayType extends DataType implements
ComplexDataType, NestedColumn
return false;
}
ArrayType arrayType = (ArrayType) o;
- return Objects.equals(itemType, arrayType.itemType)
- && Objects.equals(containsNull, arrayType.containsNull);
+ return Objects.equals(itemType, arrayType.itemType);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
index 4e48e5aa484..a2adc9a9fcc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
@@ -475,7 +475,7 @@ public abstract class DataType {
return MapType.of(fromCatalogType(mapType.getKeyType()),
fromCatalogType(mapType.getValueType()));
} else if (type.isArrayType()) {
org.apache.doris.catalog.ArrayType arrayType =
(org.apache.doris.catalog.ArrayType) type;
- return ArrayType.of(fromCatalogType(arrayType.getItemType()),
arrayType.getContainsNull());
+ return ArrayType.of(fromCatalogType(arrayType.getItemType()));
} else if (type.isVariantType()) {
// In the past, variant metadata used the ScalarType type.
// Now, we use VariantType, which inherits from ScalarType, as the
new metadata storage.
@@ -802,7 +802,7 @@ public abstract class DataType {
return arrayType.getItemType()
.getAllPromotions()
.stream()
- .map(promotionType -> ArrayType.of(promotionType,
arrayType.containsNull()))
+ .map(promotionType -> ArrayType.of(promotionType))
.collect(ImmutableList.toImmutableList());
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
index 4ed05adab2b..9049a2065d6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
@@ -389,8 +389,7 @@ public class TypeCoercionUtils {
public static DataType replaceSpecifiedType(DataType dataType,
Class<? extends DataType> specifiedType, DataType newType) {
if (dataType instanceof ArrayType) {
- return ArrayType.of(replaceSpecifiedType(((ArrayType)
dataType).getItemType(), specifiedType, newType),
- ((ArrayType) dataType).containsNull());
+ return ArrayType.of(replaceSpecifiedType(((ArrayType)
dataType).getItemType(), specifiedType, newType));
} else if (dataType instanceof MapType) {
return MapType.of(replaceSpecifiedType(((MapType)
dataType).getKeyType(), specifiedType, newType),
replaceSpecifiedType(((MapType) dataType).getValueType(),
specifiedType, newType));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
index cb544d3d01c..44e973e851e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
@@ -35,9 +35,9 @@ public class TypeTest {
ArrayType a3 = new ArrayType(new ArrayType(Type.BIGINT, true), true);
Assert.assertFalse(Type.matchExactType(a1, a3, false));
- // containsNull differs -> matchesType fails
+ // containsNull is always true now, so a4 is equivalent to a1
ArrayType a4 = new ArrayType(new ArrayType(Type.INT, true), false);
- Assert.assertFalse(Type.matchExactType(a1, a4, false));
+ Assert.assertTrue(Type.matchExactType(a1, a4, false));
// array nested decimal test
ArrayType a5 = new ArrayType(new
ArrayType(ScalarType.createDecimalV3Type(8, 2), true), true);
@@ -63,7 +63,7 @@ public class TypeTest {
Assert.assertFalse(Type.matchExactType(m1, m3, false));
Assert.assertFalse(Type.matchExactType(m1, m3, true));
- // key/value containsNull differs -> doesn't matter for matching
+ // key/value containsNull differs, but MapType.equals() ignores it ->
matches
MapType m4 = new MapType(Type.INT, arrayOfD, false, true);
Assert.assertTrue(Type.matchExactType(m1, m4, false));
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java
index fcb64ff0bfa..4964414e6b3 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java
@@ -64,7 +64,7 @@ class LiteralTest {
for (int i = 0; i < elementsArray.length; ++i) {
elementsArray[i] = i;
}
- DataType arrayType = ArrayType.of(IntegerType.INSTANCE, true);
+ DataType arrayType = ArrayType.of(IntegerType.INSTANCE);
PGenericType.Builder childTypeBuilder = PGenericType.newBuilder();
childTypeBuilder.setId(TypeId.INT32);
PGenericType.Builder typeBuilder = PGenericType.newBuilder();
@@ -94,8 +94,8 @@ class LiteralTest {
for (int i = 0; i < elementsArray.length; ++i) {
elementsArray[i] = i;
}
- DataType nestedArrayType = ArrayType.of(IntegerType.INSTANCE, true);
- DataType outArrayType = ArrayType.of(nestedArrayType, true);
+ DataType nestedArrayType = ArrayType.of(IntegerType.INSTANCE);
+ DataType outArrayType = ArrayType.of(nestedArrayType);
PGenericType.Builder childTypeBuilder = PGenericType.newBuilder();
childTypeBuilder.setId(TypeId.INT32);
PGenericType.Builder typeBuilder = PGenericType.newBuilder();
@@ -134,8 +134,8 @@ class LiteralTest {
for (int i = 0; i < elementsArray.length; ++i) {
elementsArray[i] = i;
}
- DataType nestedArrayType = ArrayType.of(IntegerType.INSTANCE, true);
- DataType outArrayType = ArrayType.of(nestedArrayType, true);
+ DataType nestedArrayType = ArrayType.of(IntegerType.INSTANCE);
+ DataType outArrayType = ArrayType.of(nestedArrayType);
PGenericType.Builder childTypeBuilder = PGenericType.newBuilder();
childTypeBuilder.setId(TypeId.INT32);
PGenericType.Builder typeBuilder = PGenericType.newBuilder();
@@ -182,7 +182,7 @@ class LiteralTest {
elementsArray[i] = i;
nullMap[i] = (i % 2 == 1);
}
- DataType arrayType = ArrayType.of(IntegerType.INSTANCE, true);
+ DataType arrayType = ArrayType.of(IntegerType.INSTANCE);
PGenericType.Builder childTypeBuilder = PGenericType.newBuilder();
childTypeBuilder.setId(TypeId.INT32);
PGenericType.Builder typeBuilder = PGenericType.newBuilder();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]