This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch branch-1.2-lts
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-1.2-lts by this push:
     new 23832b9513 [feature](array-type) Unique table support array value 
(#17024) (#19371)
23832b9513 is described below

commit 23832b951383b2d3548fb79ba69442e600fff281
Author: hqx871 <[email protected]>
AuthorDate: Mon Aug 14 23:06:26 2023 +0800

    [feature](array-type) Unique table support array value (#17024) (#19371)
    
    - #17024
    - #19751
---
 .../aggregate_function_reader_first_last.h         |  25 +-
 be/src/vec/columns/column_complex.h                |  16 +-
 be/src/vec/functions/function_case.h               |   1 +
 be/src/vec/utils/template_helpers.hpp              |   2 +
 be/test/CMakeLists.txt                             |   1 +
 .../vec/aggregate_functions/agg_replace_test.cpp   | 462 +++++++++++++++++++++
 be/test/vec/core/column_complex_test.cpp           |   7 +
 .../org/apache/doris/analysis/CreateTableStmt.java |   7 +-
 .../data/query_p0/show/complex_unique_1.csv        |   2 +
 .../data/query_p0/show/complex_unique_2.csv        |   2 +
 .../query_p0/show/test_complex_type_unique_key.out |  15 +
 .../show/test_complex_type_unique_key.groovy       |  99 +++++
 12 files changed, 615 insertions(+), 24 deletions(-)

diff --git 
a/be/src/vec/aggregate_functions/aggregate_function_reader_first_last.h 
b/be/src/vec/aggregate_functions/aggregate_function_reader_first_last.h
index 4b7c1e0c98..8529dd0cfb 100644
--- a/be/src/vec/aggregate_functions/aggregate_function_reader_first_last.h
+++ b/be/src/vec/aggregate_functions/aggregate_function_reader_first_last.h
@@ -44,12 +44,12 @@ public:
         return false;
     }
 
-    StringRef get_value() const {
+    void insert_into(IColumn& to) const {
         if constexpr (arg_is_nullable) {
             auto* col = assert_cast<const ColumnNullable*>(_ptr);
-            return assert_cast<const 
ColVecType&>(col->get_nested_column()).get_data_at(_offset);
+            assert_cast<ColVecType&>(to).insert_from(col->get_nested_column(), 
_offset);
         } else {
-            return assert_cast<const ColVecType*>(_ptr)->get_data_at(_offset);
+            assert_cast<ColVecType&>(to).insert_from(*_ptr, _offset);
         }
     }
 
@@ -71,7 +71,7 @@ protected:
 template <typename ColVecType, bool arg_is_nullable>
 struct CopiedValue : public Value<ColVecType, arg_is_nullable> {
 public:
-    StringRef get_value() const { return _copied_value; }
+    void insert_into(IColumn& to) const { 
assert_cast<ColVecType&>(to).insert(_copied_value); }
 
     bool is_null() const { return this->_ptr == nullptr; }
 
@@ -86,17 +86,16 @@ public:
                 this->reset();
                 return;
             } else {
-                _copied_value = assert_cast<const 
ColVecType&>(col->get_nested_column())
-                                        .get_data_at(row)
-                                        .to_string();
+                auto& nested_col = assert_cast<const 
ColVecType&>(col->get_nested_column());
+                nested_col.get(row, _copied_value);
             }
         } else {
-            _copied_value = assert_cast<const 
ColVecType*>(column)->get_data_at(row).to_string();
+            column->get(row, _copied_value);
         }
     }
 
 private:
-    std::string _copied_value;
+    Field _copied_value;
 };
 
 template <typename ColVecType, bool result_is_nullable, bool arg_is_nullable, 
bool is_copy>
@@ -118,15 +117,11 @@ public:
                 col.insert_default();
             } else {
                 auto& col = assert_cast<ColumnNullable&>(to);
-                //get_value will never get null value
-                const StringRef& value = _data_value.get_value();
                 col.get_null_map_data().push_back(0);
-                assert_cast<ColVecType&>(col.get_nested_column())
-                        .insert_data(value.data, value.size);
+                _data_value.insert_into(col.get_nested_column());
             }
         } else {
-            const StringRef& value = _data_value.get_value();
-            assert_cast<ColVecType&>(to).insert_data(value.data, value.size);
+            _data_value.insert_into(to);
         }
     }
 
diff --git a/be/src/vec/columns/column_complex.h 
b/be/src/vec/columns/column_complex.h
index 4b6562d534..005153853d 100644
--- a/be/src/vec/columns/column_complex.h
+++ b/be/src/vec/columns/column_complex.h
@@ -130,15 +130,19 @@ public:
 
     MutableColumnPtr clone_resized(size_t size) const override;
 
-    [[noreturn]] void insert(const Field& x) override {
-        LOG(FATAL) << "insert field not implemented";
+    void insert(const Field& x) override {
+        const String& s = doris::vectorized::get<const String&>(x);
+        data.push_back(*reinterpret_cast<const T*>(s.c_str()));
     }
 
-    [[noreturn]] Field operator[](size_t n) const override {
-        LOG(FATAL) << "operator[] not implemented";
+    Field operator[](size_t n) const override {
+        assert(n < size());
+        return Field(reinterpret_cast<const char*>(&data[n]), sizeof(data[n]));
     }
-    [[noreturn]] void get(size_t n, Field& res) const override {
-        LOG(FATAL) << "get field not implemented";
+
+    void get(size_t n, Field& res) const override {
+        assert(n < size());
+        res.assign_string(reinterpret_cast<const char*>(&data[n]), 
sizeof(data[n]));
     }
 
     [[noreturn]] UInt64 get64(size_t n) const override {
diff --git a/be/src/vec/functions/function_case.h 
b/be/src/vec/functions/function_case.h
index a4049f7840..6b628339d6 100644
--- a/be/src/vec/functions/function_case.h
+++ b/be/src/vec/functions/function_case.h
@@ -235,6 +235,7 @@ public:
 
         if constexpr (std::is_same_v<ColumnType, ColumnString> ||
                       std::is_same_v<ColumnType, ColumnBitmap> ||
+                      std::is_same_v<ColumnType, ColumnArray> ||
                       std::is_same_v<ColumnType, ColumnHLL>) {
             // result_column and all then_column is not nullable.
             // can't simd when type is string.
diff --git a/be/src/vec/utils/template_helpers.hpp 
b/be/src/vec/utils/template_helpers.hpp
index 265f74d53d..9cb93833e8 100644
--- a/be/src/vec/utils/template_helpers.hpp
+++ b/be/src/vec/utils/template_helpers.hpp
@@ -22,6 +22,7 @@
 
 #include "http/http_status.h"
 #include "vec/aggregate_functions/aggregate_function.h"
+#include "vec/columns/column_array.h"
 #include "vec/columns/column_complex.h"
 #include "vec/columns/columns_number.h"
 #include "vec/data_types/data_type.h"
@@ -54,6 +55,7 @@
     M(DateTimeV2, ColumnUInt64)
 
 #define COMPLEX_TYPE_TO_COLUMN_TYPE(M) \
+    M(Array, ColumnArray)              \
     M(BitMap, ColumnBitmap)            \
     M(HLL, ColumnHLL)
 
diff --git a/be/test/CMakeLists.txt b/be/test/CMakeLists.txt
index 40933d2634..4ff2a9998e 100644
--- a/be/test/CMakeLists.txt
+++ b/be/test/CMakeLists.txt
@@ -333,6 +333,7 @@ set(VEC_TEST_FILES
     vec/aggregate_functions/agg_collect_test.cpp
     vec/aggregate_functions/agg_test.cpp
     vec/aggregate_functions/agg_min_max_test.cpp
+    vec/aggregate_functions/agg_replace_test.cpp
     vec/aggregate_functions/vec_window_funnel_test.cpp
     vec/aggregate_functions/vec_retention_test.cpp
     vec/aggregate_functions/vec_sequence_match_test.cpp
diff --git a/be/test/vec/aggregate_functions/agg_replace_test.cpp 
b/be/test/vec/aggregate_functions/agg_replace_test.cpp
new file mode 100644
index 0000000000..cbc673340a
--- /dev/null
+++ b/be/test/vec/aggregate_functions/agg_replace_test.cpp
@@ -0,0 +1,462 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gtest/gtest.h>
+
+#include "common/logging.h"
+#include "vec/aggregate_functions/aggregate_function.h"
+#include "vec/aggregate_functions/aggregate_function_reader.h"
+#include "vec/aggregate_functions/aggregate_function_simple_factory.h"
+#include "vec/columns/column_complex.h"
+#include "vec/columns/column_vector.h"
+#include "vec/data_types/data_type_array.h"
+#include "vec/data_types/data_type_bitmap.h"
+#include "vec/data_types/data_type_date.h"
+#include "vec/data_types/data_type_date_time.h"
+#include "vec/data_types/data_type_decimal.h"
+#include "vec/data_types/data_type_hll.h"
+#include "vec/data_types/data_type_nullable.h"
+#include "vec/data_types/data_type_number.h"
+#include "vec/data_types/data_type_string.h"
+namespace doris::vectorized {
+
+class VAggReplaceTest : public testing::Test {
+public:
+    void SetUp() {
+        AggregateFunctionSimpleFactory factory = 
AggregateFunctionSimpleFactory::instance();
+        register_aggregate_function_replace_reader_load(factory);
+    }
+
+    void TearDown() {}
+
+    template <typename DataType, bool nullable>
+    void check_column_basic(const IColumn* column, int64_t expect_num, size_t 
pos = 0) {
+        //expect basic column[pos]=expect_num
+        EXPECT_FALSE(column->is_null_at(pos));
+        auto* unwrap_col = column;
+        if constexpr (nullable) {
+            auto* nullable_col = assert_cast<const 
ColumnNullable*>(unwrap_col);
+            EXPECT_FALSE(nullable_col->is_null_at(pos));
+            unwrap_col = nullable_col->get_nested_column_ptr().get();
+        }
+        if constexpr (std::is_same_v<DataType, DataTypeString>) {
+            auto str = unwrap_col->get_data_at(pos).to_string();
+            EXPECT_EQ("item" + std::to_string(expect_num), str);
+        } else if constexpr (std::is_same_v<DataType, DataTypeBitMap>) {
+            auto& container = assert_cast<const 
ColumnBitmap*>(unwrap_col)->get_data();
+            auto& bitmap = container[pos];
+            EXPECT_TRUE(bitmap.contains(static_cast<uint64_t>(expect_num)));
+        } else if constexpr (std::is_same_v<DataType, DataTypeHLL>) {
+            auto& container = assert_cast<const 
ColumnHLL*>(unwrap_col)->get_data();
+            auto& hll = container[pos];
+            auto expect = hll.estimate_cardinality();
+            
const_cast<HyperLogLog*>(&hll)->update(static_cast<uint64_t>(expect_num));
+            auto actual = hll.estimate_cardinality();
+            EXPECT_EQ(expect, actual);
+        } else {
+            EXPECT_EQ(expect_num, unwrap_col->get_int(pos));
+        }
+    }
+
+    template <typename DataType, bool nullable>
+    void check_column_array(const IColumn* column, int32_t expect_num) {
+        //expect array column:[[0..expect_num-1]]
+        EXPECT_EQ(column->size(), 1);
+        auto* unwrap_col = column;
+        if constexpr (nullable) {
+            auto* nullable_col = assert_cast<const 
ColumnNullable*>(unwrap_col);
+            EXPECT_FALSE(nullable_col->is_null_at(0));
+            unwrap_col = nullable_col->get_nested_column_ptr().get();
+        }
+        auto* array_col = assert_cast<const ColumnArray*>(unwrap_col);
+        EXPECT_EQ(array_col->get_offsets()[0], expect_num);
+        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);
+        }
+    }
+
+    template <typename DataType, bool nullable>
+    void add_elements(MutableColumnPtr& input_col, size_t input_nums) {
+        //fill column: [0..input_nums-1]
+        using FieldType = typename DataType::FieldType;
+        Field field;
+        for (size_t i = 0; i < input_nums; ++i) {
+            if constexpr (std::is_same_v<DataType, DataTypeString>) {
+                auto item = std::string("item") + std::to_string(i);
+                input_col->insert_data(item.c_str(), item.size());
+                EXPECT_EQ(item, input_col->get_data_at(i).to_string());
+            } else if constexpr (std::is_same_v<DataType, DataTypeBitMap>) {
+                BitmapValue bitmap;
+                bitmap.add(i);
+                input_col->insert_data(reinterpret_cast<const char*>(&bitmap), 
sizeof(bitmap));
+            } else if constexpr (std::is_same_v<DataType, DataTypeHLL>) {
+                HyperLogLog hll;
+                hll.update(i);
+                input_col->insert_data(reinterpret_cast<const char*>(&hll), 
sizeof(hll));
+            } else {
+                auto item = FieldType(static_cast<uint64_t>(i));
+                input_col->insert_data(reinterpret_cast<const char*>(&item), 
0);
+            }
+        }
+        EXPECT_EQ(input_col->size(), input_nums);
+    }
+
+    template <typename DataType, bool nullable>
+    void agg_replace_add_elements(MutableColumnPtr& input_col, 
AggregateFunctionPtr agg_function,
+                                  AggregateDataPtr place, size_t input_nums) {
+        //fill column: [0..input_nums-1]
+        add_elements<DataType, nullable>(input_col, input_nums);
+        const IColumn* column[1] = {input_col.get()};
+        for (int i = 0; i < input_col->size(); i++) {
+            agg_function->add(place, column, i, &_agg_arena_pool);
+        }
+    }
+
+    template <typename DataType, bool nullable>
+    void array_add_elements(MutableColumnPtr& input_col, size_t input_nums) {
+        //fill array column: [[],[0],[0,1]..[0..input_nums-1]]
+        using FieldType = typename DataType::FieldType;
+        Field field;
+        for (int32_t i = 0; i <= input_nums; ++i) {
+            doris::vectorized::Array array(i);
+            for (int32_t j = 0; j < i; ++j) {
+                if constexpr (std::is_same_v<DataType, DataTypeString>) {
+                    auto item = std::string("item") + std::to_string(j);
+                    array[j] = std::move(item);
+                } else if constexpr (IsDecimalNumber<FieldType>) {
+                    auto item = FieldType(static_cast<uint64_t>(j));
+                    array[j] = std::move(DecimalField<FieldType>(item, 20));
+                } else {
+                    array[j] = std::move(FieldType(static_cast<uint64_t>(j)));
+                }
+            }
+            input_col->insert(array);
+        }
+
+        EXPECT_EQ(input_col->size(), input_nums + 1);
+    }
+
+    template <typename DataType, bool nullable>
+    void agg_replace_array_add_elements(MutableColumnPtr& input_col,
+                                        AggregateFunctionPtr agg_function, 
AggregateDataPtr place,
+                                        size_t input_nums) {
+        //fill array column: [[],[0],[0,1]..[0..input_nums-1]]
+        array_add_elements<DataType, nullable>(input_col, input_nums);
+        const IColumn* column[1] = {input_col.get()};
+        for (size_t i = 0; i < input_col->size(); ++i) {
+            agg_function->add(place, column, i, &_agg_arena_pool);
+        }
+    }
+
+    template <typename DataType, bool nullable, bool array>
+    vectorized::DataTypePtr get_data_type() {
+        vectorized::DataTypePtr data_type = get_basic_type<DataType>();
+        if constexpr (nullable) {
+            data_type = 
std::make_shared<vectorized::DataTypeNullable>(data_type);
+        }
+        if constexpr (array) {
+            data_type = std::make_shared<vectorized::DataTypeArray>(data_type);
+            if constexpr (nullable) {
+                data_type = 
std::make_shared<vectorized::DataTypeNullable>(data_type);
+            }
+        }
+        return data_type;
+    }
+
+    template <typename DataType>
+    vectorized::DataTypePtr get_basic_type() {
+        using FieldType = typename DataType::FieldType;
+        if constexpr (IsDecimalNumber<FieldType>) {
+            //decimal column get_int will return (data * scale), so let scale 
be 1.
+            return std::make_shared<DataType>(27, 1);
+        }
+        return std::make_shared<DataType>();
+    }
+
+    template <typename DataType, bool nullable>
+    void test_agg_replace(const std::string& fn_name, size_t input_nums, 
size_t expect_num) {
+        vectorized::DataTypePtr data_type = get_data_type<DataType, nullable, 
false>();
+        DataTypes data_types = {data_type};
+        LOG(INFO) << "test_agg_replace for " << fn_name << "(" << 
data_types[0]->get_name() << ")";
+        Array array;
+        AggregateFunctionSimpleFactory factory = 
AggregateFunctionSimpleFactory::instance();
+        auto agg_function = factory.get(fn_name, data_types, array, nullable);
+        EXPECT_NE(agg_function, nullptr);
+
+        std::unique_ptr<char[]> memory(new char[agg_function->size_of_data()]);
+        AggregateDataPtr place = memory.get();
+        agg_function->create(place);
+        //EXPECT_EQ(3, 0);
+
+        auto data_column = data_type->create_column();
+        agg_replace_add_elements<DataType, nullable>(data_column, 
agg_function, place, input_nums);
+
+        //EXPECT_EQ(4, 0);
+        auto column_result = data_type->create_column();
+        agg_function->insert_result_into(place, *column_result);
+        check_column_basic<DataType, nullable>(column_result.get(), 
expect_num);
+        agg_function->destroy(place);
+    }
+
+    template <typename DataType, bool nullable>
+    void test_agg_array_replace(const std::string& fn_name, size_t input_nums, 
size_t expect_num) {
+        vectorized::DataTypePtr data_type = get_data_type<DataType, nullable, 
true>();
+        DataTypes data_types = {data_type};
+        LOG(INFO) << "test_agg_replace for " << fn_name << "(" << 
data_types[0]->get_name() << ")";
+        Array array;
+        AggregateFunctionSimpleFactory factory = 
AggregateFunctionSimpleFactory::instance();
+        auto agg_function = factory.get(fn_name, data_types, array, nullable);
+        EXPECT_NE(agg_function, nullptr);
+
+        std::unique_ptr<char[]> memory(new char[agg_function->size_of_data()]);
+        AggregateDataPtr place = memory.get();
+        agg_function->create(place);
+
+        auto input_column = data_type->create_column();
+        agg_replace_array_add_elements<DataType, nullable>(input_column, 
agg_function, place,
+                                                           input_nums);
+
+        auto column_result = data_type->create_column();
+        agg_function->insert_result_into(place, *column_result);
+        check_column_array<DataType, nullable>(column_result.get(), 
expect_num);
+
+        agg_function->destroy(place);
+    }
+
+    template <typename DataType, typename ColumnType, bool nullable>
+    void test_basic_data(int8_t input_nums) {
+        vectorized::DataTypePtr data_type = get_data_type<DataType, nullable, 
false>();
+
+        auto data_column = data_type->create_column();
+        add_elements<DataType, nullable>(data_column, input_nums);
+
+        EXPECT_EQ(input_nums, data_column->size());
+        //test Value
+        {
+            Value<ColumnType, nullable> value;
+            EXPECT_TRUE(value.is_null());
+            for (int64_t i = 0; i < input_nums; ++i) {
+                value.set_value(data_column.get(), i);
+                EXPECT_FALSE(value.is_null());
+                auto to_column = data_type->create_column();
+                if constexpr (nullable) {
+                    auto& nullable_col = 
assert_cast<ColumnNullable&>(*to_column);
+                    value.insert_into(nullable_col.get_nested_column());
+                } else {
+                    value.insert_into(*to_column);
+                }
+
+                EXPECT_EQ(1, to_column->size());
+                check_column_basic<DataType, nullable>(to_column.get(), i);
+            }
+        }
+        //test CopiedValue
+        {
+            CopiedValue<ColumnType, nullable> value;
+            EXPECT_TRUE(value.is_null());
+            for (int64_t i = 0; i < input_nums; ++i) {
+                value.set_value(data_column.get(), i);
+                EXPECT_FALSE(value.is_null());
+                auto to_column = data_type->create_column();
+                if constexpr (nullable) {
+                    auto& nullable_col = 
assert_cast<ColumnNullable&>(*to_column);
+                    value.insert_into(nullable_col.get_nested_column());
+                } else {
+                    value.insert_into(*to_column);
+                }
+                EXPECT_EQ(1, to_column->size());
+                check_column_basic<DataType, nullable>(to_column.get(), i);
+            }
+        }
+    }
+
+    template <typename DataType, typename ColumnType, bool nullable>
+    void test_array_data(int8_t input_nums) {
+        vectorized::DataTypePtr data_type = get_data_type<DataType, nullable, 
true>();
+
+        auto data_column = data_type->create_column();
+        array_add_elements<DataType, nullable>(data_column, input_nums);
+
+        EXPECT_EQ(input_nums + 1, data_column->size());
+        //test Value
+        {
+            Value<ColumnArray, nullable> value;
+            EXPECT_TRUE(value.is_null());
+            for (int64_t i = 0; i <= input_nums; ++i) {
+                value.set_value(data_column.get(), i);
+                EXPECT_FALSE(value.is_null());
+                auto to_column = data_type->create_column();
+                if constexpr (nullable) {
+                    auto& nullable_col = 
assert_cast<ColumnNullable&>(*to_column);
+                    value.insert_into(nullable_col.get_nested_column());
+                } else {
+                    value.insert_into(*to_column);
+                }
+                EXPECT_EQ(1, to_column->size());
+                check_column_array<DataType, nullable>(to_column.get(), i);
+            }
+        }
+        //test CopiedValue
+        {
+            CopiedValue<ColumnArray, nullable> value;
+            EXPECT_TRUE(value.is_null());
+            for (int64_t i = 0; i <= input_nums; ++i) {
+                value.set_value(data_column.get(), i);
+                EXPECT_FALSE(value.is_null());
+                auto to_column = data_type->create_column();
+                if constexpr (nullable) {
+                    auto& nullable_col = 
assert_cast<ColumnNullable&>(*to_column);
+                    value.insert_into(nullable_col.get_nested_column());
+                } else {
+                    value.insert_into(*to_column);
+                }
+                EXPECT_EQ(1, to_column->size());
+                check_column_array<DataType, nullable>(to_column.get(), i);
+            }
+        }
+    }
+
+private:
+    Arena _agg_arena_pool;
+};
+
+TEST_F(VAggReplaceTest, test_basic_data) {
+    test_basic_data<DataTypeInt8, ColumnInt8, false>(11);
+    test_basic_data<DataTypeInt16, ColumnInt16, false>(11);
+    test_basic_data<DataTypeInt32, ColumnInt32, false>(11);
+    test_basic_data<DataTypeInt64, ColumnInt64, false>(11);
+    test_basic_data<DataTypeInt128, ColumnInt128, false>(11);
+    test_basic_data<DataTypeDecimal<Decimal128>, ColumnDecimal128, false>(11);
+    test_basic_data<DataTypeString, ColumnString, false>(11);
+    test_basic_data<DataTypeInt128, ColumnInt128, false>(11);
+    test_basic_data<DataTypeDate, ColumnDate, false>(11);
+    test_basic_data<DataTypeDateTime, ColumnDateTime, false>(11);
+}
+
+TEST_F(VAggReplaceTest, test_array_data) {
+    test_array_data<DataTypeInt8, ColumnArray, false>(11);
+    test_array_data<DataTypeInt16, ColumnArray, false>(11);
+    test_array_data<DataTypeInt32, ColumnArray, false>(11);
+    test_array_data<DataTypeInt64, ColumnArray, false>(11);
+    test_array_data<DataTypeInt128, ColumnArray, false>(11);
+    test_array_data<DataTypeDecimal<Decimal128>, ColumnArray, false>(11);
+    test_array_data<DataTypeString, ColumnArray, false>(11);
+    test_array_data<DataTypeInt128, ColumnArray, false>(11);
+    test_array_data<DataTypeDate, ColumnArray, false>(11);
+    test_array_data<DataTypeDateTime, ColumnArray, false>(11);
+}
+
+TEST_F(VAggReplaceTest, test_basic_replace_reader) {
+    test_agg_replace<DataTypeInt8, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt16, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt32, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt64, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt128, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDecimal<Decimal128>, false>("replace_reader", 10, 
0);
+    test_agg_replace<DataTypeString, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDate, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDateTime, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDateTime, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeBitMap, false>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeHLL, false>("replace_reader", 10, 0);
+
+    test_agg_replace<DataTypeInt8, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt16, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt32, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt64, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeInt128, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDecimal<Decimal128>, true>("replace_reader", 10, 
0);
+    test_agg_replace<DataTypeString, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDate, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeDateTime, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeBitMap, true>("replace_reader", 10, 0);
+    test_agg_replace<DataTypeHLL, true>("replace_reader", 10, 0);
+}
+
+TEST_F(VAggReplaceTest, test_basic_replace_load) {
+    test_agg_replace<DataTypeInt8, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt16, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt32, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt64, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt128, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDecimal<Decimal128>, false>("replace_load", 10, 
9);
+    test_agg_replace<DataTypeString, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDate, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDateTime, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeBitMap, false>("replace_load", 10, 9);
+    test_agg_replace<DataTypeHLL, false>("replace_load", 10, 9);
+
+    test_agg_replace<DataTypeInt8, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt16, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt32, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt64, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeInt128, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDecimal<Decimal128>, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeString, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDate, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeDateTime, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeBitMap, true>("replace_load", 10, 9);
+    test_agg_replace<DataTypeHLL, true>("replace_load", 10, 9);
+}
+
+TEST_F(VAggReplaceTest, test_array_replace_reader) {
+    test_agg_array_replace<DataTypeInt8, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt16, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt32, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt64, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt128, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDecimal<Decimal128>, 
false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeString, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDate, false>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDateTime, false>("replace_reader", 10, 0);
+
+    test_agg_array_replace<DataTypeInt8, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt16, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt32, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt64, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeInt128, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDecimal<Decimal128>, 
true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeString, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDate, true>("replace_reader", 10, 0);
+    test_agg_array_replace<DataTypeDateTime, true>("replace_reader", 10, 0);
+}
+
+TEST_F(VAggReplaceTest, test_array_replace_load) {
+    test_agg_array_replace<DataTypeInt8, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt16, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt32, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt64, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt128, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDecimal<Decimal128>, false>("replace_load", 
10, 10);
+    test_agg_array_replace<DataTypeString, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDate, false>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDateTime, false>("replace_load", 10, 10);
+
+    test_agg_array_replace<DataTypeInt8, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt16, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt32, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt64, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeInt128, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDecimal<Decimal128>, true>("replace_load", 
10, 10);
+    test_agg_array_replace<DataTypeString, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDate, true>("replace_load", 10, 10);
+    test_agg_array_replace<DataTypeDateTime, true>("replace_load", 10, 10);
+}
+
+} // namespace doris::vectorized
diff --git a/be/test/vec/core/column_complex_test.cpp 
b/be/test/vec/core/column_complex_test.cpp
index 87e5998fa3..3c597ca1b9 100644
--- a/be/test/vec/core/column_complex_test.cpp
+++ b/be/test/vec/core/column_complex_test.cpp
@@ -104,6 +104,13 @@ TEST_F(ColumnBitmapTest, SerializeAndDeserialize) {
     data[row_size - 1].add(33333);
     data[row_size - 1].add(0);
     check_serialize_and_deserialize(column);
+
+    Field field;
+    column->get(0, field);
+    auto str = field.get<String>();
+    auto* bitmap = reinterpret_cast<const BitmapValue*>(str.c_str());
+    EXPECT_TRUE(bitmap->contains(10));
+    EXPECT_TRUE(bitmap->contains(1000000));
 }
 
 } // namespace doris::vectorized
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateTableStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateTableStmt.java
index d68a80f3da..347f6c555b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateTableStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateTableStmt.java
@@ -438,10 +438,11 @@ public class CreateTableStmt extends DdlStmt {
             columnDef.analyze(engineName.equals("olap"));
 
             if (columnDef.getType().isArrayType() && 
engineName.equals("olap")) {
-                if (keysDesc.getKeysType() == KeysType.UNIQUE_KEYS) {
-                    throw new AnalysisException("Array column can't be used in 
unique table");
+                if (keysDesc.getKeysType() == KeysType.AGG_KEYS) {
+                    throw new AnalysisException("Array column can't be used in 
aggregate table");
                 }
-                if (columnDef.getAggregateType() != null && 
columnDef.getAggregateType() != AggregateType.NONE) {
+                if (columnDef.getAggregateType() != null && 
columnDef.getAggregateType() != AggregateType.NONE
+                        && columnDef.getAggregateType() != 
AggregateType.REPLACE) {
                     throw new AnalysisException("Array column can't support 
aggregation "
                             + columnDef.getAggregateType());
                 }
diff --git a/regression-test/data/query_p0/show/complex_unique_1.csv 
b/regression-test/data/query_p0/show/complex_unique_1.csv
new file mode 100644
index 0000000000..285573913f
--- /dev/null
+++ b/regression-test/data/query_p0/show/complex_unique_1.csv
@@ -0,0 +1,2 @@
+1      [1, 2, 3]       [2022-07-13]
+2      []      [2023-12-24]
diff --git a/regression-test/data/query_p0/show/complex_unique_2.csv 
b/regression-test/data/query_p0/show/complex_unique_2.csv
new file mode 100644
index 0000000000..221dcbd8eb
--- /dev/null
+++ b/regression-test/data/query_p0/show/complex_unique_2.csv
@@ -0,0 +1,2 @@
+3      [1, 2, 3]       [2022-07-13]
+4      []      [2023-12-24]
diff --git 
a/regression-test/data/query_p0/show/test_complex_type_unique_key.out 
b/regression-test/data/query_p0/show/test_complex_type_unique_key.out
new file mode 100644
index 0000000000..5df6bd3ee4
--- /dev/null
+++ b/regression-test/data/query_p0/show/test_complex_type_unique_key.out
@@ -0,0 +1,15 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select --
+1      [1, 2, 3]       [2022-07-13]
+2      []      [2023-12-24]
+
+-- !select --
+1      [1, 2, 3]       [2022-07-13]
+2      []      [2023-12-24]
+
+-- !select --
+1      [1, 2, 3]       [2022-07-13]
+2      []      [2023-12-24]
+3      [1, 2, 3]       [2022-07-13]
+4      []      [2023-12-24]
+
diff --git 
a/regression-test/suites/query_p0/show/test_complex_type_unique_key.groovy 
b/regression-test/suites/query_p0/show/test_complex_type_unique_key.groovy
new file mode 100644
index 0000000000..ab72d4205a
--- /dev/null
+++ b/regression-test/suites/query_p0/show/test_complex_type_unique_key.groovy
@@ -0,0 +1,99 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_complex_type_unique_key", "p0") {
+    // define a sql table
+    def testTable = "tbl_test_complex_unique"
+    def dataFile = "complex_unique_1.csv"
+    def dataFile1 = "complex_unique_2.csv"
+
+    sql "DROP TABLE IF EXISTS ${testTable}"
+
+    sql """
+        CREATE TABLE IF NOT EXISTS tbl_test_complex_unique (
+            id INT,
+            a ARRAY<SMALLINT> NOT NULL COMMENT "",
+            an ARRAY<DATE>
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 10
+        PROPERTIES("replication_num" = "1");
+        """
+
+    // insert into valid json rows
+    sql """INSERT INTO ${testTable} VALUES (2, [],  ['2023-12-24']);"""
+    sql """INSERT INTO ${testTable} VALUES (1, [1, 2, 3], ['2022-07-13'])"""
+
+    // check result
+    qt_select "SELECT * FROM ${testTable} ORDER BY id"
+
+    // stream_load for same key
+    streamLoad {
+        table testTable
+
+        file dataFile // import csv file
+        time 10000 // limit inflight 10s
+        set 'strict_mode', 'true'
+
+        // if declared a check callback, the default check condition will 
ignore.
+        // So you must check all condition
+        check { result, exception, startTime, endTime ->
+            if (exception != null) {
+                throw exception
+            }
+            log.info("Stream load result: ${result}".toString())
+            def json = parseJson(result)
+            assertEquals("success", json.Status.toLowerCase())
+            assertEquals(2, json.NumberTotalRows)
+            assertEquals(2, json.NumberLoadedRows)
+            assertEquals(0, json.NumberFilteredRows)
+            assertTrue(json.LoadBytes > 0)
+        }
+    }
+
+    sql """sync"""
+    // check result : now only 2 rows
+    qt_select "SELECT * FROM ${testTable} ORDER BY id"
+
+    // stream load different key
+    streamLoad {
+        table testTable
+
+        file dataFile1 // import csv file
+        time 10000 // limit inflight 10s
+        set 'strict_mode', 'true'
+
+        // if declared a check callback, the default check condition will 
ignore.
+        // So you must check all condition
+        check { result, exception, startTime, endTime ->
+            if (exception != null) {
+                throw exception
+            }
+            log.info("Stream load result: ${result}".toString())
+            def json = parseJson(result)
+            assertEquals("success", json.Status.toLowerCase())
+            assertEquals(2, json.NumberTotalRows)
+            assertEquals(2, json.NumberLoadedRows)
+            assertEquals(0, json.NumberFilteredRows)
+            assertTrue(json.LoadBytes > 0)
+        }
+    }
+
+    sql """sync"""
+    // check result : now 4 rows
+    qt_select "SELECT * FROM ${testTable} ORDER BY id"
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to