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

jianliangqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 1ef22d7f7c [Feature](variant) add variant type (#24170)
1ef22d7f7c is described below

commit 1ef22d7f7c612091bdebaf33f86f48f80f6bf2a8
Author: lihangyu <[email protected]>
AuthorDate: Thu Sep 14 14:21:53 2023 +0800

    [Feature](variant) add variant type (#24170)
    
    Add variant type for metadata Add persistent information for variant, 
including the path of variant sub-columns, persisting them to the segment 
footer and tablet schema of the rowset.
---
 be/src/olap/tablet_schema.cpp                      | 62 ++++++++++++++-
 be/src/olap/tablet_schema.h                        | 67 +++++++++++++---
 be/src/olap/types.cpp                              |  6 +-
 be/src/olap/types.h                                | 13 ++++
 be/src/runtime/descriptors.cpp                     |  1 +
 be/src/runtime/descriptors.h                       |  2 +
 be/src/runtime/primitive_type.cpp                  |  5 ++
 be/src/runtime/types.cpp                           |  7 --
 be/src/runtime/types.h                             |  3 +-
 be/src/vec/data_types/data_type_factory.cpp        | 61 ++++++++++++---
 be/src/vec/data_types/data_type_factory.hpp        |  5 ++
 be/src/vec/json/path_in_data.cpp                   | 67 ++++++++++++++++
 be/src/vec/json/path_in_data.h                     |  9 +++
 .../org/apache/doris/catalog/PrimitiveType.java    | 45 ++++++++++-
 .../java/org/apache/doris/catalog/ScalarType.java  | 16 +++-
 .../main/java/org/apache/doris/catalog/Type.java   | 77 ++++++++++++++++---
 .../apache/doris/analysis/SchemaChangeExpr.java    | 89 ----------------------
 .../org/apache/doris/planner/FileLoadScanNode.java |  8 +-
 gensrc/proto/olap_file.proto                       |  2 +
 gensrc/proto/segment_v2.proto                      | 29 +++++++
 gensrc/thrift/Descriptors.thrift                   |  2 +
 .../suites/nereids_arith_p0/bitmap.groovy          |  2 +-
 .../forbid_unexpected_type.groovy                  |  4 +-
 .../join/test_bitmap_filter_nereids.groovy         |  2 +-
 24 files changed, 436 insertions(+), 148 deletions(-)

diff --git a/be/src/olap/tablet_schema.cpp b/be/src/olap/tablet_schema.cpp
index f643531d68..6f25b20e65 100644
--- a/be/src/olap/tablet_schema.cpp
+++ b/be/src/olap/tablet_schema.cpp
@@ -100,6 +100,8 @@ FieldType TabletColumn::get_field_type_by_string(const 
std::string& type_str) {
         type = FieldType::OLAP_FIELD_TYPE_STRING;
     } else if (0 == upper_type_str.compare("JSONB")) {
         type = FieldType::OLAP_FIELD_TYPE_JSONB;
+    } else if (0 == upper_type_str.compare("VARIANT")) {
+        type = FieldType::OLAP_FIELD_TYPE_VARIANT;
     } else if (0 == upper_type_str.compare("BOOLEAN")) {
         type = FieldType::OLAP_FIELD_TYPE_BOOL;
     } else if (0 == upper_type_str.compare(0, 3, "HLL")) {
@@ -230,6 +232,9 @@ std::string 
TabletColumn::get_string_by_field_type(FieldType type) {
     case FieldType::OLAP_FIELD_TYPE_JSONB:
         return "JSONB";
 
+    case FieldType::OLAP_FIELD_TYPE_VARIANT:
+        return "VARIANT";
+
     case FieldType::OLAP_FIELD_TYPE_STRING:
         return "STRING";
 
@@ -328,6 +333,7 @@ uint32_t 
TabletColumn::get_field_length_by_type(TPrimitiveType::type type, uint3
     case TPrimitiveType::AGG_STATE:
         return string_length + sizeof(OLAP_VARCHAR_MAX_LENGTH);
     case TPrimitiveType::STRING:
+    case TPrimitiveType::VARIANT:
         return string_length + sizeof(OLAP_STRING_MAX_LENGTH);
     case TPrimitiveType::JSONB:
         return string_length + sizeof(OLAP_JSONB_MAX_LENGTH);
@@ -393,6 +399,7 @@ void TabletColumn::init_from_thrift(const TColumn& tcolumn) 
{
 void TabletColumn::init_from_pb(const ColumnPB& column) {
     _unique_id = column.unique_id();
     _col_name = column.name();
+    _col_name_lower_case = to_lower(_col_name);
     _type = TabletColumn::get_field_type_by_string(column.type());
     _is_key = column.is_key();
     _is_nullable = column.is_nullable();
@@ -444,6 +451,10 @@ void TabletColumn::init_from_pb(const ColumnPB& column) {
         child_column.init_from_pb(column.children_columns(i));
         add_sub_column(child_column);
     }
+    if (column.has_column_path_info()) {
+        _column_path.from_protobuf(column.column_path_info());
+        _parent_col_unique_id = 
column.column_path_info().parrent_column_unique_id();
+    }
 }
 
 void TabletColumn::to_schema_pb(ColumnPB* column) const {
@@ -484,11 +495,17 @@ void TabletColumn::to_schema_pb(ColumnPB* column) const {
         ColumnPB* child = column->add_children_columns();
         _sub_columns[i].to_schema_pb(child);
     }
+
+    // set parts info
+    if (!_column_path.empty()) {
+        // CHECK_GT(_parent_col_unique_id, 0);
+        _column_path.to_protobuf(column->mutable_column_path_info(), 
_parent_col_unique_id);
+    }
 }
 
 void TabletColumn::add_sub_column(TabletColumn& sub_column) {
     _sub_columns.push_back(sub_column);
-    sub_column._parent = this;
+    sub_column._parent_col_unique_id = this->_unique_id;
     _sub_column_count += 1;
 }
 
@@ -523,6 +540,14 @@ vectorized::AggregateFunctionPtr 
TabletColumn::get_aggregate_function(std::strin
     return nullptr;
 }
 
+void TabletColumn::set_path_info(const vectorized::PathInData& path) {
+    _column_path = path;
+}
+
+vectorized::DataTypePtr TabletColumn::get_vec_type() const {
+    return vectorized::DataTypeFactory::instance().create_data_type(*this);
+}
+
 void TabletIndex::init_from_thrift(const TOlapTableIndex& index,
                                    const TabletSchema& tablet_schema) {
     _index_id = index.index_id;
@@ -614,13 +639,21 @@ void TabletIndex::to_schema_pb(TabletIndexPB* index) 
const {
     }
 }
 
-void TabletSchema::append_column(TabletColumn column, bool is_dropped_column) {
+void TabletSchema::append_column(TabletColumn column, ColumnType col_type) {
     if (column.is_key()) {
         _num_key_columns++;
     }
     if (column.is_nullable()) {
         _num_null_columns++;
     }
+    if (column.is_variant_type()) {
+        ++_num_variant_columns;
+        if (column.path_info().empty()) {
+            const std::string& col_name = column.name_lower_case();
+            vectorized::PathInData path(col_name);
+            column.set_path_info(path);
+        }
+    }
     if (UNLIKELY(column.name() == DELETE_SIGN)) {
         _delete_sign_idx = _num_columns;
     } else if (UNLIKELY(column.name() == SEQUENCE_COL)) {
@@ -630,8 +663,10 @@ void TabletSchema::append_column(TabletColumn column, bool 
is_dropped_column) {
     }
     // The dropped column may have same name with exsiting column, so that
     // not add to name to index map, only for uid to index map
-    if (!is_dropped_column) {
+    if (col_type == ColumnType::NORMAL) {
         _field_name_to_index[column.name()] = _num_columns;
+    } else if (col_type == ColumnType::VARIANT) {
+        _field_path_to_index[column.path_info()] = _num_columns;
     }
     _field_id_to_index[column.unique_id()] = _num_columns;
     _cols.push_back(std::move(column));
@@ -654,9 +689,11 @@ void TabletSchema::remove_index(int64_t index_id) {
 }
 
 void TabletSchema::clear_columns() {
+    _field_path_to_index.clear();
     _field_name_to_index.clear();
     _field_id_to_index.clear();
     _num_columns = 0;
+    _num_variant_columns = 0;
     _num_null_columns = 0;
     _num_key_columns = 0;
     _cols.clear();
@@ -666,6 +703,7 @@ void TabletSchema::init_from_pb(const TabletSchemaPB& 
schema) {
     SCOPED_MEM_COUNT(&_mem_size);
     _keys_type = schema.keys_type();
     _num_columns = 0;
+    _num_variant_columns = 0;
     _num_key_columns = 0;
     _num_null_columns = 0;
     _cols.clear();
@@ -684,6 +722,9 @@ void TabletSchema::init_from_pb(const TabletSchemaPB& 
schema) {
         if (column.is_nullable()) {
             _num_null_columns++;
         }
+        if (column.is_variant_type()) {
+            ++_num_variant_columns;
+        }
         _field_name_to_index[column.name()] = _num_columns;
         _field_id_to_index[column.unique_id()] = _num_columns;
         _cols.emplace_back(std::move(column));
@@ -770,6 +811,7 @@ void TabletSchema::build_current_tablet_schema(int64_t 
index_id, int32_t version
     // copy from table_schema_param
     _schema_version = version;
     _num_columns = 0;
+    _num_variant_columns = 0;
     _num_key_columns = 0;
     _num_null_columns = 0;
     bool has_bf_columns = false;
@@ -788,6 +830,9 @@ void TabletSchema::build_current_tablet_schema(int64_t 
index_id, int32_t version
         if (column->is_bf_column()) {
             has_bf_columns = true;
         }
+        if (column->is_variant_type()) {
+            ++_num_variant_columns;
+        }
         if (UNLIKELY(column->name() == DELETE_SIGN)) {
             _delete_sign_idx = _num_columns;
         } else if (UNLIKELY(column->name() == SEQUENCE_COL)) {
@@ -827,7 +872,7 @@ void TabletSchema::merge_dropped_columns(TabletSchemaSPtr 
src_schema) {
             // that deep copy it.
             src_col.to_schema_pb(&src_col_pb);
             TabletColumn new_col(src_col_pb);
-            append_column(new_col, /* is_dropped_column */ true);
+            append_column(new_col, TabletSchema::ColumnType::DROPPED);
         }
     }
 }
@@ -892,6 +937,11 @@ int32_t TabletSchema::field_index(const std::string& 
field_name) const {
     return (found == _field_name_to_index.end()) ? -1 : found->second;
 }
 
+int32_t TabletSchema::field_index(const vectorized::PathInData& path) const {
+    const auto& found = _field_path_to_index.find(path);
+    return (found == _field_path_to_index.end()) ? -1 : found->second;
+}
+
 int32_t TabletSchema::field_index(int32_t col_unique_id) const {
     const auto& found = _field_id_to_index.find(col_unique_id);
     return (found == _field_id_to_index.end()) ? -1 : found->second;
@@ -901,6 +951,10 @@ const std::vector<TabletColumn>& TabletSchema::columns() 
const {
     return _cols;
 }
 
+std::vector<TabletColumn>& TabletSchema::mutable_columns() {
+    return _cols;
+}
+
 const TabletColumn& TabletSchema::column(size_t ordinal) const {
     DCHECK(ordinal < _num_columns) << "ordinal:" << ordinal << ", 
_num_columns:" << _num_columns;
     return _cols[ordinal];
diff --git a/be/src/olap/tablet_schema.h b/be/src/olap/tablet_schema.h
index c98e5542f7..72d0636f6e 100644
--- a/be/src/olap/tablet_schema.h
+++ b/be/src/olap/tablet_schema.h
@@ -35,11 +35,15 @@
 #include "common/status.h"
 #include "gutil/stringprintf.h"
 #include "olap/olap_common.h"
+#include "util/string_util.h"
 #include "vec/aggregate_functions/aggregate_function.h"
+#include "vec/json/path_in_data.h"
 
 namespace doris {
 namespace vectorized {
 class Block;
+class PathInData;
+class IDataType;
 } // namespace vectorized
 
 struct OlapTableIndexSchema;
@@ -61,8 +65,12 @@ public:
 
     int32_t unique_id() const { return _unique_id; }
     void set_unique_id(int32_t id) { _unique_id = id; }
-    std::string name() const { return _col_name; }
-    void set_name(std::string col_name) { _col_name = col_name; }
+    const std::string& name() const { return _col_name; }
+    const std::string& name_lower_case() const { return _col_name_lower_case; }
+    void set_name(std::string col_name) {
+        _col_name = col_name;
+        _col_name_lower_case = to_lower(_col_name);
+    }
     FieldType type() const { return _type; }
     void set_type(FieldType type) { _type = type; }
     bool is_key() const { return _is_key; }
@@ -83,11 +91,17 @@ public:
     bool has_default_value() const { return _has_default_value; }
     std::string default_value() const { return _default_value; }
     size_t length() const { return _length; }
+    void set_length(size_t length) { _length = length; }
+    void set_default_value(const std::string& default_value) {
+        _default_value = default_value;
+        _has_default_value = true;
+    }
     size_t index_length() const { return _index_length; }
     void set_index_length(size_t index_length) { _index_length = index_length; 
}
     void set_is_key(bool is_key) { _is_key = is_key; }
     void set_is_nullable(bool is_nullable) { _is_nullable = is_nullable; }
     void set_has_default_value(bool has) { _has_default_value = has; }
+    void set_path_info(const vectorized::PathInData& path);
     FieldAggregationMethod aggregation() const { return _aggregation; }
     vectorized::AggregateFunctionPtr get_aggregate_function_union(
             vectorized::DataTypePtr type) const;
@@ -105,6 +119,7 @@ public:
 
     uint32_t get_subtype_count() const { return _sub_column_count; }
     const TabletColumn& get_sub_column(uint32_t i) const { return 
_sub_columns[i]; }
+    const std::vector<TabletColumn>& get_sub_columns() const { return 
_sub_columns; }
 
     friend bool operator==(const TabletColumn& a, const TabletColumn& b);
     friend bool operator!=(const TabletColumn& a, const TabletColumn& b);
@@ -117,10 +132,17 @@ public:
     bool is_row_store_column() const;
     std::string get_aggregation_name() const { return _aggregation_name; }
     bool get_result_is_nullable() const { return _result_is_nullable; }
+    const vectorized::PathInData& path_info() const { return _column_path; }
+    // If it is an extracted column from variant column
+    bool is_extracted_column() const { return !_column_path.empty() && 
_parent_col_unique_id > 0; };
+    int32_t parent_unique_id() const { return _parent_col_unique_id; }
+    void set_parent_unique_id(int32_t col_unique_id) { _parent_col_unique_id = 
col_unique_id; }
+    std::shared_ptr<const vectorized::IDataType> get_vec_type() const;
 
 private:
-    int32_t _unique_id;
+    int32_t _unique_id = -1;
     std::string _col_name;
+    std::string _col_name_lower_case;
     FieldType _type;
     bool _is_key = false;
     FieldAggregationMethod _aggregation;
@@ -141,12 +163,12 @@ private:
 
     bool _has_bitmap_index = false;
     bool _visible = true;
-
-    TabletColumn* _parent = nullptr;
+    int32_t _parent_col_unique_id = -1;
     std::vector<TabletColumn> _sub_columns;
     uint32_t _sub_column_count = 0;
 
     bool _result_is_nullable = false;
+    vectorized::PathInData _column_path;
 };
 
 bool operator==(const TabletColumn& a, const TabletColumn& b);
@@ -199,13 +221,14 @@ private:
 
 class TabletSchema {
 public:
+    enum ColumnType { NORMAL = 0, DROPPED = 1, VARIANT = 2 };
     // TODO(yingchun): better to make constructor as private to avoid
     // manually init members incorrectly, and define a new function like
     // void create_from_pb(const TabletSchemaPB& schema, TabletSchema* 
tablet_schema).
     TabletSchema() = default;
     void init_from_pb(const TabletSchemaPB& schema);
     void to_schema_pb(TabletSchemaPB* tablet_meta_pb) const;
-    void append_column(TabletColumn column, bool is_dropped_column = false);
+    void append_column(TabletColumn column, ColumnType col_type = 
ColumnType::NORMAL);
     void append_index(TabletIndex index);
     void remove_index(int64_t index_id);
     // Must make sure the row column is always the last column
@@ -213,20 +236,22 @@ public:
     void copy_from(const TabletSchema& tablet_schema);
     std::string to_key() const;
     int64_t mem_size() const { return _mem_size; }
-
     size_t row_size() const;
     int32_t field_index(const std::string& field_name) const;
+    int32_t field_index(const vectorized::PathInData& path) const;
     int32_t field_index(int32_t col_unique_id) const;
     const TabletColumn& column(size_t ordinal) const;
     const TabletColumn& column(const std::string& field_name) const;
     Status have_column(const std::string& field_name) const;
     const TabletColumn& column_by_uid(int32_t col_unique_id) const;
     const std::vector<TabletColumn>& columns() const;
+    std::vector<TabletColumn>& mutable_columns();
     size_t num_columns() const { return _num_columns; }
     size_t num_key_columns() const { return _num_key_columns; }
     size_t num_null_columns() const { return _num_null_columns; }
     size_t num_short_key_columns() const { return _num_short_key_columns; }
     size_t num_rows_per_row_block() const { return _num_rows_per_row_block; }
+    size_t num_variant_columns() const { return _num_variant_columns; };
     KeysType keys_type() const { return _keys_type; }
     SortType sort_type() const { return _sort_type; }
     size_t sort_col_num() const { return _sort_col_num; }
@@ -305,6 +330,27 @@ public:
         str += "]";
         return str;
     }
+
+    // Dump [(name, type, is_nullable), ...]
+    string dump_structure() const {
+        string str = "[";
+        for (auto p : _field_name_to_index) {
+            if (str.size() > 1) {
+                str += ", ";
+            }
+            str += "(";
+            str += p.first;
+            str += ", ";
+            str += 
TabletColumn::get_string_by_field_type(_cols[p.second].type());
+            str += ", ";
+            str += "is_nullable:";
+            str += (_cols[p.second].is_nullable() ? "true" : "false");
+            str += ")";
+        }
+        str += "]";
+        return str;
+    }
+
     vectorized::Block create_missing_columns_block();
     vectorized::Block create_update_columns_block();
     void set_partial_update_info(bool is_partial_update,
@@ -317,8 +363,8 @@ public:
     }
     void set_is_strict_mode(bool is_strict_mode) { _is_strict_mode = 
is_strict_mode; }
     bool is_strict_mode() const { return _is_strict_mode; }
-    std::vector<uint32_t> get_missing_cids() { return _missing_cids; }
-    std::vector<uint32_t> get_update_cids() { return _update_cids; }
+    std::vector<uint32_t> get_missing_cids() const { return _missing_cids; }
+    std::vector<uint32_t> get_update_cids() const { return _update_cids; }
 
 private:
     friend bool operator==(const TabletSchema& a, const TabletSchema& b);
@@ -331,7 +377,10 @@ private:
     std::vector<TabletIndex> _indexes;
     std::unordered_map<std::string, int32_t> _field_name_to_index;
     std::unordered_map<int32_t, int32_t> _field_id_to_index;
+    std::unordered_map<vectorized::PathInData, int32_t, 
vectorized::PathInData::Hash>
+            _field_path_to_index;
     size_t _num_columns = 0;
+    size_t _num_variant_columns = 0;
     size_t _num_key_columns = 0;
     size_t _num_null_columns = 0;
     size_t _num_short_key_columns = 0;
diff --git a/be/src/olap/types.cpp b/be/src/olap/types.cpp
index 102f00c7e1..2c92bd3f2c 100644
--- a/be/src/olap/types.cpp
+++ b/be/src/olap/types.cpp
@@ -96,8 +96,9 @@ const TypeInfo* get_scalar_type_info(FieldType field_type) {
             get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_DECIMAL64>(),
             get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_DECIMAL128I>(),
             get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_JSONB>(),
-            nullptr,
-            get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_AGG_STATE>()};
+            get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_VARIANT>(),
+            get_scalar_type_info<FieldType::OLAP_FIELD_TYPE_AGG_STATE>(),
+            nullptr};
     return field_type_array[int(field_type)];
 }
 
@@ -168,6 +169,7 @@ const TypeInfo* get_array_type_info(FieldType leaf_type, 
int32_t iterations) {
             INIT_ARRAY_TYPE_INFO_LIST(FieldType::OLAP_FIELD_TYPE_DECIMAL64),
             INIT_ARRAY_TYPE_INFO_LIST(FieldType::OLAP_FIELD_TYPE_DECIMAL128I),
             INIT_ARRAY_TYPE_INFO_LIST(FieldType::OLAP_FIELD_TYPE_JSONB),
+            INIT_ARRAY_TYPE_INFO_LIST(FieldType::OLAP_FIELD_TYPE_VARIANT),
             {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, 
nullptr, nullptr},
             INIT_ARRAY_TYPE_INFO_LIST(FieldType::OLAP_FIELD_TYPE_AGG_STATE)};
     return array_type_Info_arr[int(leaf_type)][iterations];
diff --git a/be/src/olap/types.h b/be/src/olap/types.h
index 40d812a160..bb54959aee 100644
--- a/be/src/olap/types.h
+++ b/be/src/olap/types.h
@@ -731,6 +731,10 @@ struct CppTypeTraits<FieldType::OLAP_FIELD_TYPE_JSONB> {
     using CppType = Slice;
 };
 template <>
+struct CppTypeTraits<FieldType::OLAP_FIELD_TYPE_VARIANT> {
+    using CppType = Slice;
+};
+template <>
 struct CppTypeTraits<FieldType::OLAP_FIELD_TYPE_HLL> {
     using CppType = Slice;
 };
@@ -1385,6 +1389,15 @@ struct FieldTypeTraits<FieldType::OLAP_FIELD_TYPE_JSONB>
     }
 };
 
+template <>
+struct FieldTypeTraits<FieldType::OLAP_FIELD_TYPE_VARIANT>
+        : public FieldTypeTraits<FieldType::OLAP_FIELD_TYPE_JSONB> {
+    static int cmp(const void* left, const void* right) {
+        LOG(WARNING) << "can not compare VARIANT values";
+        return -1; // always update ?
+    }
+};
+
 template <>
 struct FieldTypeTraits<FieldType::OLAP_FIELD_TYPE_HLL>
         : public FieldTypeTraits<FieldType::OLAP_FIELD_TYPE_VARCHAR> {};
diff --git a/be/src/runtime/descriptors.cpp b/be/src/runtime/descriptors.cpp
index 73bde2ba9d..5b58ff2af5 100644
--- a/be/src/runtime/descriptors.cpp
+++ b/be/src/runtime/descriptors.cpp
@@ -64,6 +64,7 @@ SlotDescriptor::SlotDescriptor(const TSlotDescriptor& tdesc)
           _is_materialized(tdesc.isMaterialized),
           _is_key(tdesc.is_key),
           _need_materialize(tdesc.need_materialize),
+          _column_paths(tdesc.column_paths),
           _is_auto_increment(tdesc.__isset.is_auto_increment ? 
tdesc.is_auto_increment : false) {}
 
 SlotDescriptor::SlotDescriptor(const PSlotDescriptor& pdesc)
diff --git a/be/src/runtime/descriptors.h b/be/src/runtime/descriptors.h
index af59b287c4..18d9bb62f3 100644
--- a/be/src/runtime/descriptors.h
+++ b/be/src/runtime/descriptors.h
@@ -108,6 +108,7 @@ public:
 
     bool is_key() const { return _is_key; }
     bool need_materialize() const { return _need_materialize; }
+    const std::vector<std::string>& column_paths() const { return 
_column_paths; };
 
     bool is_auto_increment() const { return _is_auto_increment; }
 
@@ -143,6 +144,7 @@ private:
 
     const bool _is_key;
     const bool _need_materialize;
+    const std::vector<std::string> _column_paths;
 
     const bool _is_auto_increment;
 
diff --git a/be/src/runtime/primitive_type.cpp 
b/be/src/runtime/primitive_type.cpp
index f45e13b745..82a189107d 100644
--- a/be/src/runtime/primitive_type.cpp
+++ b/be/src/runtime/primitive_type.cpp
@@ -152,6 +152,8 @@ PrimitiveType thrift_to_type(TPrimitiveType::type ttype) {
     case TPrimitiveType::AGG_STATE:
         return TYPE_AGG_STATE;
 
+    case TPrimitiveType::VARIANT:
+        return TYPE_VARIANT;
     default:
         CHECK(false) << ", meet unknown type " << ttype;
         return INVALID_TYPE;
@@ -217,6 +219,9 @@ TPrimitiveType::type to_thrift(PrimitiveType ptype) {
     case TYPE_JSONB:
         return TPrimitiveType::JSONB;
 
+    case TYPE_VARIANT:
+        return TPrimitiveType::VARIANT;
+
     case TYPE_BINARY:
         return TPrimitiveType::BINARY;
 
diff --git a/be/src/runtime/types.cpp b/be/src/runtime/types.cpp
index 1b8870286e..4cb3d3ef5b 100644
--- a/be/src/runtime/types.cpp
+++ b/be/src/runtime/types.cpp
@@ -92,13 +92,6 @@ TypeDescriptor::TypeDescriptor(const std::vector<TTypeNode>& 
types, int* idx)
         }
         break;
     }
-    case TTypeNodeType::VARIANT: {
-        DCHECK(!node.__isset.scalar_type);
-        // variant column must be the last column
-        DCHECK_EQ(*idx, types.size() - 1);
-        type = TYPE_VARIANT;
-        break;
-    }
     case TTypeNodeType::MAP: {
         //TODO(xy): handle contains_null[0] for key and [1] for value
         DCHECK(!node.__isset.scalar_type);
diff --git a/be/src/runtime/types.h b/be/src/runtime/types.h
index 19766933e8..bb030b66d6 100644
--- a/be/src/runtime/types.h
+++ b/be/src/runtime/types.h
@@ -227,8 +227,7 @@ struct TypeDescriptor {
     }
 
     bool is_complex_type() const {
-        return type == TYPE_STRUCT || type == TYPE_ARRAY || type == TYPE_MAP ||
-               type == TYPE_VARIANT;
+        return type == TYPE_STRUCT || type == TYPE_ARRAY || type == TYPE_MAP;
     }
 
     bool is_collection_type() const { return type == TYPE_ARRAY || type == 
TYPE_MAP; }
diff --git a/be/src/vec/data_types/data_type_factory.cpp 
b/be/src/vec/data_types/data_type_factory.cpp
index 971b37eb8f..4ab836141b 100644
--- a/be/src/vec/data_types/data_type_factory.cpp
+++ b/be/src/vec/data_types/data_type_factory.cpp
@@ -37,6 +37,7 @@
 #include "common/exception.h"
 #include "common/status.h"
 #include "data_type_time.h"
+#include "gen_cpp/segment_v2.pb.h"
 #include "olap/field.h"
 #include "olap/olap_common.h"
 #include "runtime/define_primitive_type.h"
@@ -103,7 +104,7 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TabletColumn& col_desc, bool
     }
 
     if ((is_nullable || col_desc.is_nullable()) && nested) {
-        return std::make_shared<DataTypeNullable>(nested);
+        return make_nullable(nested);
     }
     return nested;
 }
@@ -152,6 +153,8 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TypeDescriptor& col_desc, bo
     case TYPE_DOUBLE:
         nested = std::make_shared<vectorized::DataTypeFloat64>();
         break;
+    case TYPE_VARIANT:
+        return std::make_shared<vectorized::DataTypeObject>("", true);
     case TYPE_STRING:
     case TYPE_CHAR:
     case TYPE_VARCHAR:
@@ -220,9 +223,6 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TypeDescriptor& col_desc, bo
         nested = std::make_shared<DataTypeStruct>(dataTypes, names);
         break;
     }
-    case TYPE_VARIANT:
-        // ColumnObject always none nullable
-        return std::make_shared<vectorized::DataTypeObject>("json", true);
     case INVALID_TYPE:
     default:
         throw Exception(ErrorCode::INTERNAL_ERROR, "invalid PrimitiveType: 
{}", (int)col_desc.type);
@@ -230,7 +230,7 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TypeDescriptor& col_desc, bo
     }
 
     if (nested && is_nullable) {
-        return std::make_shared<vectorized::DataTypeNullable>(nested);
+        return make_nullable(nested);
     }
     return nested;
 }
@@ -286,6 +286,8 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TypeIndex& type_index, bool
     case TypeIndex::String:
         nested = std::make_shared<vectorized::DataTypeString>();
         break;
+    case TypeIndex::VARIANT:
+        return std::make_shared<vectorized::DataTypeObject>("", true);
     case TypeIndex::Decimal32:
         nested = 
std::make_shared<DataTypeDecimal<Decimal32>>(BeConsts::MAX_DECIMAL32_PRECISION, 
0);
         break;
@@ -322,7 +324,7 @@ DataTypePtr DataTypeFactory::create_data_type(const 
TypeIndex& type_index, bool
     }
 
     if (nested && is_nullable) {
-        return std::make_shared<vectorized::DataTypeNullable>(nested);
+        return make_nullable(nested);
     }
     return nested;
 }
@@ -372,6 +374,8 @@ DataTypePtr 
DataTypeFactory::_create_primitive_data_type(const FieldType& type,
     case FieldType::OLAP_FIELD_TYPE_STRING:
         result = std::make_shared<vectorized::DataTypeString>();
         break;
+    case FieldType::OLAP_FIELD_TYPE_VARIANT:
+        return std::make_shared<vectorized::DataTypeObject>("", true);
     case FieldType::OLAP_FIELD_TYPE_JSONB:
         result = std::make_shared<vectorized::DataTypeJsonb>();
         break;
@@ -442,6 +446,8 @@ DataTypePtr DataTypeFactory::create_data_type(const 
PColumnMeta& pcolumn) {
     case PGenericType::STRING:
         nested = std::make_shared<DataTypeString>();
         break;
+    case PGenericType::VARIANT:
+        return std::make_shared<DataTypeObject>("", true);
     case PGenericType::JSONB:
         nested = std::make_shared<DataTypeJsonb>();
         break;
@@ -506,10 +512,6 @@ DataTypePtr DataTypeFactory::create_data_type(const 
PColumnMeta& pcolumn) {
         nested = std::make_shared<DataTypeStruct>(dataTypes, names);
         break;
     }
-    case PGenericType::VARIANT: {
-        nested = std::make_shared<DataTypeObject>("object", true);
-        break;
-    }
     case PGenericType::QUANTILE_STATE: {
         nested = std::make_shared<DataTypeQuantileState>();
         break;
@@ -535,7 +537,42 @@ DataTypePtr DataTypeFactory::create_data_type(const 
PColumnMeta& pcolumn) {
     }
 
     if (nested && pcolumn.is_nullable() > 0) {
-        return std::make_shared<vectorized::DataTypeNullable>(nested);
+        return make_nullable(nested);
+    }
+    return nested;
+}
+
+DataTypePtr DataTypeFactory::create_data_type(const segment_v2::ColumnMetaPB& 
pcolumn) {
+    DataTypePtr nested = nullptr;
+    if (pcolumn.type() == static_cast<int>(FieldType::OLAP_FIELD_TYPE_ARRAY)) {
+        // Item subcolumn and length subcolumn
+        DCHECK_GE(pcolumn.children_columns().size(), 2) << 
pcolumn.DebugString();
+        nested = 
std::make_shared<DataTypeArray>(create_data_type(pcolumn.children_columns(0)));
+    } else if (pcolumn.type() == 
static_cast<int>(FieldType::OLAP_FIELD_TYPE_MAP)) {
+        DCHECK_GE(pcolumn.children_columns().size(), 2) << 
pcolumn.DebugString();
+        nested = std::make_shared<vectorized::DataTypeMap>(
+                create_data_type(pcolumn.children_columns(0)),
+                create_data_type(pcolumn.children_columns(1)));
+    } else if (pcolumn.type() == 
static_cast<int>(FieldType::OLAP_FIELD_TYPE_STRUCT)) {
+        DCHECK_GE(pcolumn.children_columns().size(), 1);
+        size_t col_size = pcolumn.children_columns().size();
+        DataTypes dataTypes;
+        Strings names;
+        dataTypes.reserve(col_size);
+        names.reserve(col_size);
+        for (size_t i = 0; i < col_size; i++) {
+            dataTypes.push_back(create_data_type(pcolumn.children_columns(i)));
+            names.push_back("");
+        }
+        nested = std::make_shared<DataTypeStruct>(dataTypes, names);
+    } else {
+        // TODO add precision and frac
+        nested = 
_create_primitive_data_type(static_cast<FieldType>(pcolumn.type()),
+                                             pcolumn.precision(), 
pcolumn.frac());
+    }
+
+    if (pcolumn.is_nullable() && nested) {
+        return make_nullable(nested);
     }
     return nested;
 }
@@ -623,7 +660,7 @@ DataTypePtr DataTypeFactory::create_data_type(const 
arrow::DataType* type, bool
     }
 
     if (nested && is_nullable) {
-        return std::make_shared<vectorized::DataTypeNullable>(nested);
+        return make_nullable(nested);
     }
     return nested;
 }
diff --git a/be/src/vec/data_types/data_type_factory.hpp 
b/be/src/vec/data_types/data_type_factory.hpp
index 6bef55938f..0439be0277 100644
--- a/be/src/vec/data_types/data_type_factory.hpp
+++ b/be/src/vec/data_types/data_type_factory.hpp
@@ -38,6 +38,10 @@ class Field;
 class PColumnMeta;
 enum class FieldType;
 
+namespace segment_v2 {
+class ColumnMetaPB;
+}
+
 namespace vectorized {
 enum class TypeIndex;
 } // namespace vectorized
@@ -62,6 +66,7 @@ public:
     DataTypePtr create_data_type(const TypeDescriptor& col_desc, bool 
is_nullable = true);
 
     DataTypePtr create_data_type(const PColumnMeta& pcolumn);
+    DataTypePtr create_data_type(const segment_v2::ColumnMetaPB& pcolumn);
 
     DataTypePtr create_data_type(const arrow::DataType* type, bool 
is_nullable);
 
diff --git a/be/src/vec/json/path_in_data.cpp b/be/src/vec/json/path_in_data.cpp
index a05542e189..3eab57f781 100644
--- a/be/src/vec/json/path_in_data.cpp
+++ b/be/src/vec/json/path_in_data.cpp
@@ -45,6 +45,15 @@ PathInData::PathInData(const Parts& parts_) {
 PathInData::PathInData(const PathInData& other) : path(other.path) {
     build_parts(other.get_parts());
 }
+
+PathInData::PathInData(const std::vector<std::string>& paths) {
+    PathInDataBuilder path_builder;
+    for (size_t i = 0; i < paths.size(); ++i) {
+        path_builder.append(paths[i], false);
+    }
+    build_parts(path_builder.get_parts());
+}
+
 PathInData& PathInData::operator=(const PathInData& other) {
     if (this != &other) {
         path = other.path;
@@ -92,10 +101,66 @@ void PathInData::build_parts(const Parts& other_parts) {
         begin += part.key.length() + 1;
     }
 }
+
+void PathInData::from_protobuf(const segment_v2::ColumnPathInfo& pb) {
+    parts.clear();
+    path = pb.path();
+    has_nested = pb.has_has_nested();
+    parts.reserve(pb.path_part_infos().size());
+    for (const segment_v2::ColumnPathPartInfo& part_info : 
pb.path_part_infos()) {
+        Part part;
+        part.is_nested = part_info.is_nested();
+        part.anonymous_array_level = part_info.anonymous_array_level();
+        part.key = part_info.key();
+        parts.push_back(part);
+    }
+}
+
+std::string PathInData::to_jsonpath() const {
+    std::string jsonpath = "$.";
+    if (parts.empty()) {
+        return jsonpath;
+    }
+    auto it = parts.begin();
+    jsonpath += it->key;
+    ++it;
+    for (; it != parts.end(); ++it) {
+        jsonpath += ".";
+        jsonpath += it->key;
+    }
+    return jsonpath;
+}
+
+void PathInData::to_protobuf(segment_v2::ColumnPathInfo* pb, int32_t 
parent_col_unique_id) const {
+    pb->set_path(path);
+    pb->set_has_nested(has_nested);
+    pb->set_parrent_column_unique_id(parent_col_unique_id);
+
+    // set parts info
+    for (const Part& part : parts) {
+        segment_v2::ColumnPathPartInfo& part_info = *pb->add_path_part_infos();
+        part_info.set_key(std::string(part.key.data(), part.key.size()));
+        part_info.set_is_nested(part.is_nested);
+        part_info.set_anonymous_array_level(part.anonymous_array_level);
+    }
+}
+
 size_t PathInData::Hash::operator()(const PathInData& value) const {
     auto hash = get_parts_hash(value.parts);
     return hash.low ^ hash.high;
 }
+
+PathInData PathInData::pop_front() const {
+    PathInData new_path;
+    Parts new_parts;
+    if (!parts.empty()) {
+        std::copy(parts.begin() + 1, parts.end(), 
std::back_inserter(new_parts));
+    }
+    new_path.build_path(new_parts);
+    new_path.build_parts(new_parts);
+    return new_path;
+}
+
 PathInDataBuilder& PathInDataBuilder::append(std::string_view key, bool 
is_array) {
     if (parts.empty()) {
         current_anonymous_array_level += is_array;
@@ -125,9 +190,11 @@ PathInDataBuilder& PathInDataBuilder::append(const 
PathInData::Parts& path, bool
     }
     return *this;
 }
+
 void PathInDataBuilder::pop_back() {
     parts.pop_back();
 }
+
 void PathInDataBuilder::pop_back(size_t n) {
     assert(n <= parts.size());
     parts.resize(parts.size() - n);
diff --git a/be/src/vec/json/path_in_data.h b/be/src/vec/json/path_in_data.h
index 47da9c85cb..87278c1c92 100644
--- a/be/src/vec/json/path_in_data.h
+++ b/be/src/vec/json/path_in_data.h
@@ -27,6 +27,7 @@
 #include <string_view>
 #include <vector>
 
+#include "gen_cpp/segment_v2.pb.h"
 #include "vec/common/uint128.h"
 #include "vec/core/field.h"
 #include "vec/core/types.h"
@@ -59,6 +60,7 @@ public:
     PathInData() = default;
     explicit PathInData(std::string_view path_);
     explicit PathInData(const Parts& parts_);
+    explicit PathInData(const std::vector<std::string>& paths);
     PathInData(const PathInData& other);
     PathInData& operator=(const PathInData& other);
     static UInt128 get_parts_hash(const Parts& parts_);
@@ -71,6 +73,11 @@ public:
     struct Hash {
         size_t operator()(const PathInData& value) const;
     };
+    std::string to_jsonpath() const;
+
+    PathInData pop_front() const;
+    void to_protobuf(segment_v2::ColumnPathInfo* pb, int32_t 
parent_col_unique_id) const;
+    void from_protobuf(const segment_v2::ColumnPathInfo& pb);
 
 private:
     /// Creates full path from parts.
@@ -90,8 +97,10 @@ public:
     const PathInData::Parts& get_parts() const { return parts; }
     PathInDataBuilder& append(std::string_view key, bool is_array);
     PathInDataBuilder& append(const PathInData::Parts& path, bool is_array);
+    PathInDataBuilder& append(const std::vector<std::string>& parts);
     void pop_back();
     void pop_back(size_t n);
+    PathInData build() { return PathInData(parts); }
 
 private:
     PathInData::Parts parts;
diff --git 
a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
index 50fddda0f3..5f34bb65c9 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
@@ -128,6 +128,7 @@ public enum PrimitiveType {
         builder.put(NULL_TYPE, VARCHAR);
         builder.put(NULL_TYPE, STRING);
         builder.put(NULL_TYPE, JSONB);
+        builder.put(NULL_TYPE, VARIANT);
         builder.put(NULL_TYPE, BITMAP); //TODO(weixiang):why null type can 
cast to bitmap?
         builder.put(NULL_TYPE, TIME);
         builder.put(NULL_TYPE, TIMEV2);
@@ -414,6 +415,7 @@ public enum PrimitiveType {
         builder.put(VARCHAR, DECIMAL128);
         builder.put(VARCHAR, VARCHAR);
         builder.put(VARCHAR, JSONB);
+        builder.put(VARCHAR, VARIANT);
         builder.put(VARCHAR, STRING);
         builder.put(VARCHAR, TIME);
         builder.put(VARCHAR, TIMEV2);
@@ -437,6 +439,7 @@ public enum PrimitiveType {
         builder.put(STRING, DECIMAL128);
         builder.put(STRING, VARCHAR);
         builder.put(STRING, JSONB);
+        builder.put(STRING, VARIANT);
         builder.put(STRING, STRING);
         builder.put(STRING, TIME);
         builder.put(STRING, TIMEV2);
@@ -503,8 +506,26 @@ public enum PrimitiveType {
         builder.put(DECIMAL128, STRING);
 
         // JSONB
+        builder.put(JSONB, BOOLEAN);
+        builder.put(JSONB, TINYINT);
+        builder.put(JSONB, SMALLINT);
+        builder.put(JSONB, INT);
+        builder.put(JSONB, BIGINT);
+        builder.put(JSONB, LARGEINT);
+        builder.put(JSONB, FLOAT);
+        builder.put(JSONB, DOUBLE);
+        builder.put(JSONB, DECIMALV2);
+        builder.put(JSONB, DECIMAL32);
+        builder.put(JSONB, DECIMAL64);
+        builder.put(JSONB, DECIMAL128);
         builder.put(JSONB, VARCHAR);
         builder.put(JSONB, STRING);
+        builder.put(JSONB, VARIANT);
+
+        // VARIANT
+        builder.put(VARIANT, VARCHAR);
+        builder.put(VARIANT, STRING);
+        builder.put(VARIANT, JSONB);
 
         // HLL
         builder.put(HLL, HLL);
@@ -568,6 +589,7 @@ public enum PrimitiveType {
         supportedTypes.add(DOUBLE);
         supportedTypes.add(VARCHAR);
         supportedTypes.add(JSONB);
+        supportedTypes.add(VARIANT);
         supportedTypes.add(STRING);
         supportedTypes.add(HLL);
         supportedTypes.add(CHAR);
@@ -585,7 +607,6 @@ public enum PrimitiveType {
         supportedTypes.add(ARRAY);
         supportedTypes.add(MAP);
         supportedTypes.add(QUANTILE_STATE);
-        supportedTypes.add(VARIANT);
         supportedTypes.add(AGG_STATE);
     }
 
@@ -641,6 +662,7 @@ public enum PrimitiveType {
         compatibilityMatrix[NULL_TYPE.ordinal()][CHAR.ordinal()] = CHAR;
         compatibilityMatrix[NULL_TYPE.ordinal()][VARCHAR.ordinal()] = VARCHAR;
         compatibilityMatrix[NULL_TYPE.ordinal()][JSONB.ordinal()] = JSONB;
+        compatibilityMatrix[NULL_TYPE.ordinal()][VARIANT.ordinal()] = VARIANT;
         compatibilityMatrix[NULL_TYPE.ordinal()][STRING.ordinal()] = STRING;
         compatibilityMatrix[NULL_TYPE.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[NULL_TYPE.ordinal()][DECIMAL32.ordinal()] = 
DECIMAL32;
@@ -667,6 +689,7 @@ public enum PrimitiveType {
         compatibilityMatrix[BOOLEAN.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[BOOLEAN.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][STRING.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[BOOLEAN.ordinal()][DECIMAL32.ordinal()] = 
DECIMAL32;
@@ -689,6 +712,7 @@ public enum PrimitiveType {
         compatibilityMatrix[TINYINT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[TINYINT.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][STRING.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[TINYINT.ordinal()][DECIMAL32.ordinal()] = 
DECIMAL32;
@@ -710,6 +734,7 @@ public enum PrimitiveType {
         compatibilityMatrix[SMALLINT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][JSONB.ordinal()] = 
INVALID_TYPE;
+        compatibilityMatrix[SMALLINT.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][STRING.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[SMALLINT.ordinal()][DECIMAL32.ordinal()] = 
DECIMAL32;
@@ -730,6 +755,7 @@ public enum PrimitiveType {
         compatibilityMatrix[INT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][VARCHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[INT.ordinal()][VARIANT.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][STRING.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][DECIMALV2.ordinal()] = DECIMALV2;
         compatibilityMatrix[INT.ordinal()][DECIMAL32.ordinal()] = DECIMAL32;
@@ -749,6 +775,7 @@ public enum PrimitiveType {
         compatibilityMatrix[BIGINT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[BIGINT.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][STRING.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][DECIMALV2.ordinal()] = DECIMALV2;
         compatibilityMatrix[BIGINT.ordinal()][DECIMAL32.ordinal()] = DECIMAL32;
@@ -767,6 +794,7 @@ public enum PrimitiveType {
         compatibilityMatrix[LARGEINT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][JSONB.ordinal()] = 
INVALID_TYPE;
+        compatibilityMatrix[LARGEINT.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][STRING.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[LARGEINT.ordinal()][DECIMAL32.ordinal()] = 
DECIMAL32;
@@ -784,6 +812,7 @@ public enum PrimitiveType {
         compatibilityMatrix[FLOAT.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][VARCHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[FLOAT.ordinal()][VARIANT.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][STRING.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][DECIMALV2.ordinal()] = DECIMALV2;
         compatibilityMatrix[FLOAT.ordinal()][DECIMAL32.ordinal()] = DECIMAL32;
@@ -800,6 +829,7 @@ public enum PrimitiveType {
         compatibilityMatrix[DOUBLE.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[DOUBLE.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][STRING.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][DECIMALV2.ordinal()] = DECIMALV2;
         compatibilityMatrix[DOUBLE.ordinal()][DECIMAL32.ordinal()] = DECIMAL32;
@@ -816,6 +846,7 @@ public enum PrimitiveType {
         compatibilityMatrix[DATE.ordinal()][VARCHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][STRING.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][JSONB.ordinal()] = INVALID_TYPE;
+        compatibilityMatrix[DATE.ordinal()][VARIANT.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][DECIMALV2.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][DECIMAL32.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][DECIMAL64.ordinal()] = 
INVALID_TYPE;
@@ -842,6 +873,7 @@ public enum PrimitiveType {
         compatibilityMatrix[DATETIME.ordinal()][CHAR.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][VARCHAR.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][JSONB.ordinal()] = 
INVALID_TYPE;
+        compatibilityMatrix[DATETIME.ordinal()][VARIANT.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][STRING.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][DECIMALV2.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][DECIMAL32.ordinal()] = 
INVALID_TYPE;
@@ -865,6 +897,7 @@ public enum PrimitiveType {
         compatibilityMatrix[CHAR.ordinal()][CHAR.ordinal()] = CHAR;
         compatibilityMatrix[CHAR.ordinal()][VARCHAR.ordinal()] = VARCHAR;
         compatibilityMatrix[CHAR.ordinal()][JSONB.ordinal()] = CHAR;
+        compatibilityMatrix[CHAR.ordinal()][VARIANT.ordinal()] = CHAR;
         compatibilityMatrix[CHAR.ordinal()][STRING.ordinal()] = STRING;
         compatibilityMatrix[CHAR.ordinal()][DECIMALV2.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[CHAR.ordinal()][DECIMAL32.ordinal()] = 
INVALID_TYPE;
@@ -876,6 +909,7 @@ public enum PrimitiveType {
         compatibilityMatrix[VARCHAR.ordinal()][VARCHAR.ordinal()] = VARCHAR;
         compatibilityMatrix[VARCHAR.ordinal()][STRING.ordinal()] = STRING;
         compatibilityMatrix[VARCHAR.ordinal()][JSONB.ordinal()] = VARCHAR;
+        compatibilityMatrix[VARCHAR.ordinal()][VARIANT.ordinal()] = VARCHAR;
         compatibilityMatrix[VARCHAR.ordinal()][DECIMALV2.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[VARCHAR.ordinal()][DECIMAL32.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[VARCHAR.ordinal()][DECIMAL64.ordinal()] = 
INVALID_TYPE;
@@ -889,6 +923,7 @@ public enum PrimitiveType {
         compatibilityMatrix[STRING.ordinal()][DECIMAL64.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[STRING.ordinal()][DECIMAL128.ordinal()] = 
INVALID_TYPE;
         compatibilityMatrix[STRING.ordinal()][JSONB.ordinal()] = STRING;
+        compatibilityMatrix[STRING.ordinal()][VARIANT.ordinal()] = STRING;
         compatibilityMatrix[STRING.ordinal()][TIME.ordinal()] = INVALID_TYPE;
         compatibilityMatrix[STRING.ordinal()][TIMEV2.ordinal()] = INVALID_TYPE;
 
@@ -896,6 +931,13 @@ public enum PrimitiveType {
         compatibilityMatrix[JSONB.ordinal()][STRING.ordinal()] = STRING;
         compatibilityMatrix[JSONB.ordinal()][VARCHAR.ordinal()] = VARCHAR;
         compatibilityMatrix[JSONB.ordinal()][CHAR.ordinal()] = CHAR;
+        compatibilityMatrix[JSONB.ordinal()][VARIANT.ordinal()] = VARIANT;
+
+        compatibilityMatrix[VARIANT.ordinal()][VARIANT.ordinal()] = VARIANT;
+        compatibilityMatrix[VARIANT.ordinal()][STRING.ordinal()] = STRING;
+        compatibilityMatrix[VARIANT.ordinal()][VARCHAR.ordinal()] = VARCHAR;
+        compatibilityMatrix[VARIANT.ordinal()][CHAR.ordinal()] = CHAR;
+        compatibilityMatrix[VARIANT.ordinal()][JSONB.ordinal()] = JSONB;
 
         compatibilityMatrix[DECIMALV2.ordinal()][DECIMALV2.ordinal()] = 
DECIMALV2;
         compatibilityMatrix[DECIMALV2.ordinal()][DECIMAL32.ordinal()] = 
DECIMALV2;
@@ -1233,6 +1275,7 @@ public enum PrimitiveType {
             case STRING:
                 return MysqlColType.MYSQL_TYPE_BLOB;
             case JSONB:
+            case VARIANT:
                 return MysqlColType.MYSQL_TYPE_JSON;
             case MAP:
                 return MysqlColType.MYSQL_TYPE_MAP;
diff --git 
a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
index 3655a8c5c4..b8dc17d546 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
@@ -178,6 +178,8 @@ public class ScalarType extends Type {
                 return createStringType();
             case JSONB:
                 return createJsonbType();
+            case VARIANT:
+                return createVariantType();
             case STRING:
                 return createStringType();
             case HLL:
@@ -247,6 +249,8 @@ public class ScalarType extends Type {
                 return createVarcharType();
             case "JSON":
                 return createJsonbType();
+            case "VARIANT":
+                return createVariantType();
             case "STRING":
             case "TEXT":
                 return createStringType();
@@ -509,6 +513,13 @@ public class ScalarType extends Type {
         return type;
     }
 
+    public static ScalarType createVariantType() {
+        // length checked in analysis
+        ScalarType type = new ScalarType(PrimitiveType.VARIANT);
+        type.len = MAX_STRING_LENGTH;
+        return type;
+    }
+
     public static ScalarType createVarchar(int len) {
         // length checked in analysis
         ScalarType type = new ScalarType(PrimitiveType.VARCHAR);
@@ -558,6 +569,8 @@ public class ScalarType extends Type {
             return "TEXT";
         } else if (type == PrimitiveType.JSONB) {
             return "JSON";
+        } else if (type == PrimitiveType.VARIANT) {
+            return "VARIANT";
         }
         return type.toString();
     }
@@ -679,7 +692,8 @@ public class ScalarType extends Type {
             case CHAR:
             case HLL:
             case STRING:
-            case JSONB: {
+            case JSONB:
+            case VARIANT: {
                 scalarType.setLen(getLength());
                 break;
             }
diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
index 77ac485c75..d48b82ef16 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
@@ -113,7 +113,7 @@ public abstract class Type {
     public static final StructType GENERIC_STRUCT = new 
StructType(Lists.newArrayList(
             new StructField("generic_struct", new 
ScalarType(PrimitiveType.NULL_TYPE))));
     public static final StructType STRUCT = new StructType();
-    public static final VariantType VARIANT = new VariantType();
+    public static final ScalarType VARIANT = new 
ScalarType(PrimitiveType.VARIANT);
     public static final AnyType ANY_STRUCT_TYPE = new AnyStructType();
     public static final AnyType ANY_ELEMENT_TYPE = new AnyElementType();
 
@@ -172,6 +172,7 @@ public abstract class Type {
         trivialTypes.add(TIME);
         trivialTypes.add(TIMEV2);
         trivialTypes.add(JSONB);
+        trivialTypes.add(VARIANT);
 
         supportedTypes = Lists.newArrayList();
         supportedTypes.addAll(trivialTypes);
@@ -439,19 +440,23 @@ public abstract class Type {
         return isScalarType(PrimitiveType.JSONB);
     }
 
+    public boolean isVariantType() {
+        return isScalarType(PrimitiveType.VARIANT);
+    }
+
     // only metric types have the following constraint:
     // 1. don't support as key column
     // 2. don't support filter
     // 3. don't support group by
     // 4. don't support index
     public boolean isOnlyMetricType() {
-        return isObjectStored() || isComplexType() || isJsonbType();
+        return isObjectStored() || isComplexType() || isJsonbType() || 
isVariantType();
     }
 
     public static final String OnlyMetricTypeErrorMsg =
-            "Doris hll, bitmap, array, map, struct, jsonb column must use with 
specific function, and don't"
+            "Doris hll, bitmap, array, map, struct, jsonb, variant column must 
use with specific function, and don't"
                     + " support filter, group by or order by. please run 'help 
hll' or 'help bitmap' or 'help array'"
-                    + " or 'help map' or 'help struct' or 'help jsonb' in your 
mysql client.";
+                    + " or 'help map' or 'help struct' or 'help jsonb' or 
'help variant' in your mysql client.";
 
     public boolean isHllType() {
         return isScalarType(PrimitiveType.HLL);
@@ -544,10 +549,6 @@ public abstract class Type {
         return isStructType() || isCollectionType();
     }
 
-    public boolean isVariantType() {
-        return this instanceof VariantType;
-    }
-
     public boolean isCollectionType() {
         return isMapType() || isArrayType() || isMultiRowType();
     }
@@ -715,7 +716,10 @@ public abstract class Type {
     }
 
     public static boolean canCastTo(Type sourceType, Type targetType) {
-        if (sourceType.isScalarType() && targetType.isScalarType()) {
+        if (sourceType.isVariantType() && (targetType.isScalarType() || 
targetType.isArrayType())) {
+            // variant could cast to scalar types and array
+            return true;
+        } else if (sourceType.isScalarType() && targetType.isScalarType()) {
             return ScalarType.canCastTo((ScalarType) sourceType, (ScalarType) 
targetType);
         } else if (sourceType.isArrayType() && targetType.isArrayType()) {
             return ArrayType.canCastTo((ArrayType) sourceType, (ArrayType) 
targetType);
@@ -889,6 +893,8 @@ public abstract class Type {
                 return Type.VARCHAR;
             case JSONB:
                 return Type.JSONB;
+            case VARIANT:
+                return Type.VARIANT;
             case STRING:
                 return Type.STRING;
             case HLL:
@@ -903,8 +909,6 @@ public abstract class Type {
                 return Type.BITMAP;
             case QUANTILE_STATE:
                 return Type.QUANTILE_STATE;
-            case VARIANT:
-                return new VariantType();
             case LAMBDA_FUNCTION:
                 return Type.LAMBDA_FUNCTION;
             case AGG_STATE:
@@ -1260,6 +1264,7 @@ public abstract class Type {
         compatibilityMatrix[BOOLEAN.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[BOOLEAN.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][DECIMAL32.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BOOLEAN.ordinal()][DECIMAL64.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1290,6 +1295,7 @@ public abstract class Type {
         compatibilityMatrix[TINYINT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[TINYINT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[TINYINT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1316,6 +1322,7 @@ public abstract class Type {
         compatibilityMatrix[SMALLINT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[SMALLINT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[SMALLINT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1345,6 +1352,7 @@ public abstract class Type {
         compatibilityMatrix[INT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[INT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[INT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1375,6 +1383,7 @@ public abstract class Type {
         compatibilityMatrix[BIGINT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[BIGINT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BIGINT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1397,6 +1406,7 @@ public abstract class Type {
         compatibilityMatrix[LARGEINT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[LARGEINT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[LARGEINT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1418,6 +1428,7 @@ public abstract class Type {
         compatibilityMatrix[FLOAT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[FLOAT.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[FLOAT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1435,6 +1446,7 @@ public abstract class Type {
         compatibilityMatrix[DOUBLE.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DOUBLE.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][DATEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DOUBLE.ordinal()][DATETIMEV2.ordinal()] = 
PrimitiveType.DOUBLE;
@@ -1457,6 +1469,7 @@ public abstract class Type {
         compatibilityMatrix[DATE.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DATE.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATE.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1476,6 +1489,7 @@ public abstract class Type {
         compatibilityMatrix[DATEV2.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATEV2.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATEV2.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DATEV2.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATEV2.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATEV2.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1494,6 +1508,7 @@ public abstract class Type {
         compatibilityMatrix[DATETIME.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DATETIME.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIME.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1512,6 +1527,7 @@ public abstract class Type {
         compatibilityMatrix[DATETIMEV2.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIMEV2.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIMEV2.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DATETIMEV2.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIMEV2.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DATETIMEV2.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1530,6 +1546,7 @@ public abstract class Type {
         compatibilityMatrix[CHAR.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[CHAR.ordinal()][STRING.ordinal()] = 
PrimitiveType.STRING;
         compatibilityMatrix[CHAR.ordinal()][JSONB.ordinal()] = 
PrimitiveType.CHAR;
+        compatibilityMatrix[CHAR.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.CHAR;
         compatibilityMatrix[CHAR.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[CHAR.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1546,6 +1563,7 @@ public abstract class Type {
         compatibilityMatrix[VARCHAR.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[VARCHAR.ordinal()][STRING.ordinal()] = 
PrimitiveType.STRING;
         compatibilityMatrix[VARCHAR.ordinal()][JSONB.ordinal()] = 
PrimitiveType.VARCHAR;
+        compatibilityMatrix[VARCHAR.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.VARCHAR;
         compatibilityMatrix[VARCHAR.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[VARCHAR.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
@@ -1561,6 +1579,7 @@ public abstract class Type {
         compatibilityMatrix[STRING.ordinal()][DECIMAL64.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[STRING.ordinal()][DECIMAL128.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[STRING.ordinal()][JSONB.ordinal()] = 
PrimitiveType.STRING;
+        compatibilityMatrix[STRING.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.STRING;
         compatibilityMatrix[STRING.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
         //JSONB
@@ -1579,6 +1598,22 @@ public abstract class Type {
         compatibilityMatrix[JSONB.ordinal()][CHAR.ordinal()] = 
PrimitiveType.CHAR;
         compatibilityMatrix[JSONB.ordinal()][VARCHAR.ordinal()] = 
PrimitiveType.VARCHAR;
         compatibilityMatrix[JSONB.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        // VARIANT
+        compatibilityMatrix[VARIANT.ordinal()][DECIMALV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][DECIMAL32.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][DECIMAL64.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][DECIMAL128.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][HLL.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][TIME.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][TIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][DATEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][DATETIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[VARIANT.ordinal()][STRING.ordinal()] = 
PrimitiveType.STRING;
+        compatibilityMatrix[VARIANT.ordinal()][CHAR.ordinal()] = 
PrimitiveType.CHAR;
+        compatibilityMatrix[VARIANT.ordinal()][VARCHAR.ordinal()] = 
PrimitiveType.VARCHAR;
+        compatibilityMatrix[VARIANT.ordinal()][AGG_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
 
         // DECIMALV2
         compatibilityMatrix[DECIMALV2.ordinal()][HLL.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1589,6 +1624,7 @@ public abstract class Type {
         compatibilityMatrix[DECIMALV2.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DECIMALV2.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DECIMALV2.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[DECIMALV2.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DECIMALV2.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DECIMALV2.ordinal()][DECIMAL32.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[DECIMALV2.ordinal()][DECIMAL64.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1645,6 +1681,7 @@ public abstract class Type {
         compatibilityMatrix[HLL.ordinal()][BITMAP.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[HLL.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[HLL.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[HLL.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[HLL.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[HLL.ordinal()][DECIMAL32.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[HLL.ordinal()][DECIMAL64.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1657,6 +1694,7 @@ public abstract class Type {
         compatibilityMatrix[BITMAP.ordinal()][TIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BITMAP.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BITMAP.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[BITMAP.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BITMAP.ordinal()][QUANTILE_STATE.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BITMAP.ordinal()][DATEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[BITMAP.ordinal()][DATETIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1667,6 +1705,7 @@ public abstract class Type {
 
         //QUANTILE_STATE
         compatibilityMatrix[QUANTILE_STATE.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[QUANTILE_STATE.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[QUANTILE_STATE.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[QUANTILE_STATE.ordinal()][DATEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[QUANTILE_STATE.ordinal()][DATETIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1677,6 +1716,7 @@ public abstract class Type {
 
         //AGG_STATE
         compatibilityMatrix[AGG_STATE.ordinal()][JSONB.ordinal()] = 
PrimitiveType.INVALID_TYPE;
+        compatibilityMatrix[AGG_STATE.ordinal()][VARIANT.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[AGG_STATE.ordinal()][STRING.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[AGG_STATE.ordinal()][DATEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
         compatibilityMatrix[AGG_STATE.ordinal()][DATETIMEV2.ordinal()] = 
PrimitiveType.INVALID_TYPE;
@@ -1764,6 +1804,8 @@ public abstract class Type {
                 return STRING;
             case JSONB:
                 return JSONB;
+            case VARIANT:
+                return VARIANT;
             default:
                 return INVALID;
 
@@ -1806,6 +1848,19 @@ public abstract class Type {
             return Type.STRING;
         }
 
+        // VARIANT
+        if (t1ResultType == PrimitiveType.VARIANT && t2ResultType == 
PrimitiveType.VARIANT) {
+            return Type.VARIANT;
+        }
+        if ((t1ResultType == PrimitiveType.VARIANT && t2ResultType == 
PrimitiveType.VARCHAR)
+                || (t1ResultType == PrimitiveType.VARCHAR && t2ResultType == 
PrimitiveType.VARIANT)) {
+            return Type.VARCHAR;
+        }
+        if ((t1ResultType == PrimitiveType.VARIANT && t2ResultType == 
PrimitiveType.STRING)
+                || (t1ResultType == PrimitiveType.STRING && t2ResultType == 
PrimitiveType.VARIANT)) {
+            return Type.STRING;
+        }
+
         // int family type and char family type should cast to char family type
         if ((t1.getPrimitiveType().isFixedPointType() && 
t2.getPrimitiveType().isCharFamily())
                 || (t2.getPrimitiveType().isFixedPointType() && 
t1.getPrimitiveType().isCharFamily())) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaChangeExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaChangeExpr.java
deleted file mode 100644
index 84f8f4c03d..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaChangeExpr.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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.
-
-package org.apache.doris.analysis;
-
-import org.apache.doris.catalog.PrimitiveType;
-import org.apache.doris.catalog.Type;
-import org.apache.doris.catalog.VariantType;
-import org.apache.doris.common.AnalysisException;
-import org.apache.doris.thrift.TExpr;
-import org.apache.doris.thrift.TExprNode;
-import org.apache.doris.thrift.TExprNodeType;
-import org.apache.doris.thrift.TSchemaChangeExpr;
-import org.apache.doris.thrift.TTypeDesc;
-import org.apache.doris.thrift.TTypeNode;
-
-import com.google.common.base.Preconditions;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.ArrayList;
-
-public class SchemaChangeExpr extends Expr {
-    private static final Logger LOG = 
LogManager.getLogger(SchemaChangeExpr.class);
-    // Target table id
-    private int tableId;
-    private SlotRef variantSlot;
-
-    public SchemaChangeExpr(SlotRef sourceSlot, int tableId) {
-        super();
-        Preconditions.checkNotNull(sourceSlot);
-        Preconditions.checkState(sourceSlot.getType() == Type.VARIANT);
-        variantSlot = sourceSlot;
-        this.tableId = tableId;
-    }
-
-    @Override
-    protected void treeToThriftHelper(TExpr container) {
-        super.treeToThriftHelper(container);
-    }
-
-    @Override
-    protected void toThrift(TExprNode msg) {
-        TSchemaChangeExpr schemaInfo = new TSchemaChangeExpr();
-        schemaInfo.setTableId(tableId);
-        msg.setSchemaChangeExpr(schemaInfo);
-        // set src variant slot
-        variantSlot.toThrift(msg);
-        msg.node_type = TExprNodeType.SCHEMA_CHANGE_EXPR;
-        // set type info
-        TTypeDesc desc = new TTypeDesc();
-        desc.setTypes(new ArrayList<TTypeNode>());
-        VariantType variant = new VariantType();
-        variant.toThrift(desc);
-        msg.setType(desc);
-    }
-
-    @Override
-    public Expr clone() {
-        return new SchemaChangeExpr(variantSlot, tableId);
-    }
-
-    @Override
-    public String toSqlImpl() {
-        return "SCHEMA_CHANGE(" + variantSlot.toSql() + ")";
-    }
-
-    @Override
-    public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
-        Type childType = variantSlot.getType();
-        if (childType.getPrimitiveType() != PrimitiveType.VARIANT) {
-            throw new AnalysisException("Invalid column " + 
variantSlot.toSql());
-        }
-    }
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/planner/FileLoadScanNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/planner/FileLoadScanNode.java
index 17398a5491..964acab5f2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/FileLoadScanNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/FileLoadScanNode.java
@@ -25,7 +25,7 @@ import org.apache.doris.analysis.ExprSubstitutionMap;
 import org.apache.doris.analysis.FunctionCallExpr;
 import org.apache.doris.analysis.IntLiteral;
 import org.apache.doris.analysis.NullLiteral;
-import org.apache.doris.analysis.SchemaChangeExpr;
+// import org.apache.doris.analysis.SchemaChangeExpr;
 import org.apache.doris.analysis.SlotDescriptor;
 import org.apache.doris.analysis.SlotRef;
 import org.apache.doris.analysis.StringLiteral;
@@ -36,7 +36,7 @@ import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.FunctionSet;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Table;
-import org.apache.doris.catalog.TableIf;
+// import org.apache.doris.catalog.TableIf;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.UserException;
 import org.apache.doris.load.BrokerFileGroup;
@@ -313,10 +313,6 @@ public class FileLoadScanNode extends FileScanNode {
                 String name = "jsonb_parse_" + nullable + "_error_to_null";
                 expr = new FunctionCallExpr(name, args);
                 expr.analyze(analyzer);
-            } else if (dstType == PrimitiveType.VARIANT) {
-                // Generate SchemaChange expr for dynamicly generating columns
-                TableIf targetTbl = desc.getTable();
-                expr = new SchemaChangeExpr((SlotRef) expr, (int) 
targetTbl.getId());
             } else {
                 expr = castToSlot(destSlotDesc, expr);
             }
diff --git a/gensrc/proto/olap_file.proto b/gensrc/proto/olap_file.proto
index 6a21a77734..fe5f76f6b4 100644
--- a/gensrc/proto/olap_file.proto
+++ b/gensrc/proto/olap_file.proto
@@ -206,6 +206,8 @@ message ColumnPB {
     repeated ColumnPB children_columns = 17;
     repeated string children_column_names = 18;
     optional bool result_is_nullable = 19;
+    // persist info for PathInData that represents path in document, e.g. JSON.
+    optional segment_v2.ColumnPathInfo column_path_info = 20;
 }
 
 enum IndexType {
diff --git a/gensrc/proto/segment_v2.proto b/gensrc/proto/segment_v2.proto
index 9bf62846ec..c71bedd6d9 100644
--- a/gensrc/proto/segment_v2.proto
+++ b/gensrc/proto/segment_v2.proto
@@ -137,6 +137,28 @@ message ZoneMapPB {
     optional bool pass_all = 5 [default = false];
 }
 
+message ColumnPathPartInfo {
+    // key represent a part of key in full parts info
+    optional string key = 1;
+    // is_nested indicates that the part if a nested array
+    optional bool is_nested = 2;
+    // anonymous_array_level indicates the nested level of array
+    optional uint32 anonymous_array_level = 3;
+}
+
+// Persist info for PathInData that represents path in document, e.g. JSON.
+// Each variant's subcolumn must persist it's path info to storage, since it 
only
+// uses path info to identify a column in storage instead of column unique id
+message ColumnPathInfo {
+    // The full path string representation
+    optional string path = 1;
+    // A list of parts, each part indicate a sub path of the whole path
+    repeated ColumnPathPartInfo path_part_infos = 2;
+    optional bool has_nested = 3;
+    // The original parent variant's unique id, used to distinguish from 
different variants
+    optional uint32 parrent_column_unique_id = 4;
+}
+
 message ColumnMetaPB {
     // column id in table schema
     optional uint32 column_id = 1;
@@ -162,6 +184,13 @@ message ColumnMetaPB {
     optional uint64 num_rows = 11;
     repeated string children_column_names = 12;
 
+    // persist info for PathInData that represents path in document, e.g. JSON.
+    optional ColumnPathInfo column_path_info = 13;
+
+    // Extra type info to be compatible with tabet schema
+    optional bytes default_value = 14; // ColumnMessage.default_value ?
+    optional int32 precision = 15; // ColumnMessage.precision
+    optional int32 frac = 16; // ColumnMessag
 }
 
 message PrimaryKeyIndexMetaPB {
diff --git a/gensrc/thrift/Descriptors.thrift b/gensrc/thrift/Descriptors.thrift
index 1b9f85e2b7..fa391febda 100644
--- a/gensrc/thrift/Descriptors.thrift
+++ b/gensrc/thrift/Descriptors.thrift
@@ -60,6 +60,8 @@ struct TSlotDescriptor {
   // materialize them.Used to optmize to read less data and less memory usage
   13: optional bool need_materialize = true
   14: optional bool is_auto_increment = false;
+  // subcolumn path info list for semi structure column(variant)
+  15: optional list<string> column_paths
 }
 
 struct TTupleDescriptor {
diff --git a/regression-test/suites/nereids_arith_p0/bitmap.groovy 
b/regression-test/suites/nereids_arith_p0/bitmap.groovy
index 8e7f6cbf54..f7826621be 100644
--- a/regression-test/suites/nereids_arith_p0/bitmap.groovy
+++ b/regression-test/suites/nereids_arith_p0/bitmap.groovy
@@ -23,6 +23,6 @@ suite('bitmap') {
         sql "select id from (select BITMAP_EMPTY() as c0 from expr_test) as 
ref0 where c0 = 1 order by id"
         exception "can not cast from origin type BITMAP to target type=DOUBLE"
         sql "select id from expr_test group by id having ktint in (select 
BITMAP_EMPTY() from expr_test) order by id"
-        exception "Doris hll, bitmap, array, map, struct, jsonb column must 
use with specific function,"
+        exception "Doris hll, bitmap, array, map, struct, jsonb, variant 
column must use with specific function,"
     }
 }
diff --git 
a/regression-test/suites/nereids_syntax_p0/forbid_unexpected_type.groovy 
b/regression-test/suites/nereids_syntax_p0/forbid_unexpected_type.groovy
index 8f7fef423d..bf11bf18e3 100644
--- a/regression-test/suites/nereids_syntax_p0/forbid_unexpected_type.groovy
+++ b/regression-test/suites/nereids_syntax_p0/forbid_unexpected_type.groovy
@@ -48,7 +48,7 @@ suite("forbid_unexpected_type") {
         sql """
             select min(version()) over (partition by 
bitmap_union(bitmap_empty())) as c0 from forbid_unexpected_types
         """
-        exception "Doris hll, bitmap, array, map, struct, jsonb column"
+        exception "Doris hll, bitmap, array, map, struct, jsonb, variant 
column"
     }
 
     // window function's order by could not have bitmap
@@ -56,7 +56,7 @@ suite("forbid_unexpected_type") {
         sql """
             select min(version()) over (partition by 1 order by 
bitmap_union(bitmap_empty())) as c0 from forbid_unexpected_types
         """
-        exception "Doris hll, bitmap, array, map, struct, jsonb column"
+        exception "Doris hll, bitmap, array, map, struct, jsonb, variant 
column"
     }
 }
 
diff --git 
a/regression-test/suites/query_p0/join/test_bitmap_filter_nereids.groovy 
b/regression-test/suites/query_p0/join/test_bitmap_filter_nereids.groovy
index d0cf4b2384..e8e5d33d16 100644
--- a/regression-test/suites/query_p0/join/test_bitmap_filter_nereids.groovy
+++ b/regression-test/suites/query_p0/join/test_bitmap_filter_nereids.groovy
@@ -78,7 +78,7 @@ suite("test_bitmap_filter_nereids") {
 
     test {
         sql "select k1, count(*) from ${tbl1} b1 group by k1 having k1 in 
(select k2 from ${tbl2} b2) order by k1;"
-        exception "Doris hll, bitmap, array, map, struct, jsonb column must 
use with specific function, and don't support filter"
+        exception "Doris hll, bitmap, array, map, struct, jsonb, variant 
column must use with specific function, and don't support filter"
     }
 
     explain{


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

Reply via email to