| // 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 <gen_cpp/Descriptors_constants.h> | |
| #include <gen_cpp/Exprs_types.h> | |
| #include <gen_cpp/Types_types.h> | |
| #include <gen_cpp/descriptors.pb.h> | |
| #include <gtest/gtest.h> | |
| #include <memory> | |
| #include "common/exception.h" | |
| #include "core/data_type/data_type_number.h" | |
| #include "runtime/descriptors.h" | |
| #include "testutil/desc_tbl_builder.h" | |
| namespace doris { | |
| class SlotDescriptorTest : public testing::Test { | |
| public: | |
| void SetUp() override {} | |
| void TearDown() override {} | |
| protected: | |
| // Helper method to create a basic TSlotDescriptor | |
| TSlotDescriptor create_basic_slot_descriptor(int slot_id = 1, | |
| const std::string& col_name = "test_col") { | |
| TSlotDescriptor tdesc; | |
| tdesc.__set_id(slot_id); | |
| tdesc.__set_parent(1); | |
| tdesc.__set_slotType(create_string_type()); | |
| tdesc.__set_columnPos(0); | |
| tdesc.__set_colName(col_name); | |
| tdesc.__set_col_unique_id(slot_id); | |
| tdesc.__set_slotIdx(0); | |
| tdesc.__set_isMaterialized(true); | |
| tdesc.__set_is_key(false); | |
| tdesc.__set_nullIndicatorBit(0); | |
| return tdesc; | |
| } | |
| // Helper method to create a TTypeDesc for string type | |
| TTypeDesc create_string_type() { | |
| TTypeDesc type_desc; | |
| TTypeNode type_node; | |
| TScalarType scalar_type; | |
| scalar_type.__set_type(TPrimitiveType::STRING); | |
| type_node.__set_type(TTypeNodeType::SCALAR); | |
| type_node.__set_scalar_type(scalar_type); | |
| type_desc.types.push_back(type_node); | |
| return type_desc; | |
| } | |
| // Helper method to create a TExpr with nodes | |
| TExpr create_virtual_column_expr(TExprNodeType::type node_type = TExprNodeType::FUNCTION_CALL) { | |
| TExpr expr; | |
| TExprNode node; | |
| node.__set_node_type(node_type); | |
| node.__set_type(create_string_type()); | |
| expr.nodes.push_back(node); | |
| return expr; | |
| } | |
| }; | |
| TEST_F(SlotDescriptorTest, BasicConstructor) { | |
| // Test basic constructor without virtual column expression | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(); | |
| EXPECT_NO_THROW({ | |
| SlotDescriptor slot_desc(tdesc); | |
| EXPECT_EQ(slot_desc.id(), 1); | |
| EXPECT_EQ(slot_desc.col_name(), "test_col"); | |
| EXPECT_EQ(slot_desc.col_unique_id(), 1); | |
| EXPECT_FALSE(slot_desc.is_key()); | |
| EXPECT_EQ(slot_desc.get_virtual_column_expr(), nullptr); | |
| }); | |
| } | |
| TEST_F(SlotDescriptorTest, VirtualColumnExprValid) { | |
| // Test constructor with valid virtual column expression | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(); | |
| TExpr virtual_expr = create_virtual_column_expr(TExprNodeType::FUNCTION_CALL); | |
| tdesc.__set_virtual_column_expr(virtual_expr); | |
| EXPECT_NO_THROW({ | |
| SlotDescriptor slot_desc(tdesc); | |
| EXPECT_EQ(slot_desc.id(), 1); | |
| EXPECT_EQ(slot_desc.col_name(), "test_col"); | |
| EXPECT_NE(slot_desc.get_virtual_column_expr(), nullptr); | |
| }); | |
| } | |
| TEST_F(SlotDescriptorTest, VirtualColumnExprEmptyNodes) { | |
| // Test constructor with empty virtual column expression nodes - should throw exception | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(1, "virtual_col"); | |
| TExpr virtual_expr; | |
| // Empty nodes list | |
| virtual_expr.nodes.clear(); | |
| tdesc.__set_virtual_column_expr(virtual_expr); | |
| EXPECT_THROW({ SlotDescriptor slot_desc(tdesc); }, doris::Exception); | |
| // Test the specific exception message | |
| try { | |
| SlotDescriptor slot_desc(tdesc); | |
| FAIL() << "Expected doris::Exception"; | |
| } catch (const doris::Exception& e) { | |
| std::string error_msg = e.what(); | |
| EXPECT_TRUE(error_msg.find("Virtual column expr node is empty") != std::string::npos); | |
| EXPECT_TRUE(error_msg.find("virtual_col") != std::string::npos); | |
| EXPECT_TRUE(error_msg.find("col_unique_id: 1") != std::string::npos); | |
| } | |
| } | |
| TEST_F(SlotDescriptorTest, VirtualColumnExprSlotRefNode) { | |
| // Test constructor with SLOT_REF node type - should throw exception | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(2, "slot_ref_col"); | |
| TExpr virtual_expr = create_virtual_column_expr(TExprNodeType::SLOT_REF); | |
| tdesc.__set_virtual_column_expr(virtual_expr); | |
| EXPECT_THROW({ SlotDescriptor slot_desc(tdesc); }, doris::Exception); | |
| // Test the specific exception message | |
| try { | |
| SlotDescriptor slot_desc(tdesc); | |
| FAIL() << "Expected doris::Exception"; | |
| } catch (const doris::Exception& e) { | |
| std::string error_msg = e.what(); | |
| EXPECT_TRUE(error_msg.find("Virtual column expr node is slot ref") != std::string::npos); | |
| EXPECT_TRUE(error_msg.find("slot_ref_col") != std::string::npos); | |
| EXPECT_TRUE(error_msg.find("col_unique_id: 2") != std::string::npos); | |
| } | |
| } | |
| TEST_F(SlotDescriptorTest, VirtualColumnExprVirtualSlotRefNode) { | |
| // Test constructor with VIRTUAL_SLOT_REF node type - should be valid | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(); | |
| TExpr virtual_expr = create_virtual_column_expr(TExprNodeType::VIRTUAL_SLOT_REF); | |
| tdesc.__set_virtual_column_expr(virtual_expr); | |
| EXPECT_NO_THROW({ | |
| SlotDescriptor slot_desc(tdesc); | |
| EXPECT_EQ(slot_desc.id(), 1); | |
| EXPECT_EQ(slot_desc.col_name(), "test_col"); | |
| EXPECT_NE(slot_desc.get_virtual_column_expr(), nullptr); | |
| }); | |
| } | |
| TEST_F(SlotDescriptorTest, OptionalFields) { | |
| // Test constructor with optional fields set | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(); | |
| tdesc.__set_is_auto_increment(true); | |
| tdesc.__set_col_default_value("default_value"); | |
| EXPECT_NO_THROW({ | |
| SlotDescriptor slot_desc(tdesc); | |
| EXPECT_EQ(slot_desc.id(), 1); | |
| EXPECT_EQ(slot_desc.col_name(), "test_col"); | |
| }); | |
| } | |
| TEST_F(SlotDescriptorTest, OptionalFieldsNotSet) { | |
| // Test constructor with optional fields not set | |
| TSlotDescriptor tdesc = create_basic_slot_descriptor(); | |
| // Don't set is_auto_increment and col_default_value | |
| EXPECT_NO_THROW({ | |
| SlotDescriptor slot_desc(tdesc); | |
| EXPECT_EQ(slot_desc.id(), 1); | |
| EXPECT_EQ(slot_desc.col_name(), "test_col"); | |
| }); | |
| } | |
| TEST_F(SlotDescriptorTest, DebugString) { | |
| // Test debug string output for virtual and non-virtual columns | |
| TSlotDescriptor tdesc1 = create_basic_slot_descriptor(1, "normal_col"); | |
| SlotDescriptor slot_desc1(tdesc1); | |
| std::string debug_str1 = slot_desc1.debug_string(); | |
| EXPECT_TRUE(debug_str1.find("normal_col") != std::string::npos); | |
| EXPECT_TRUE(debug_str1.find("is_virtual=false") != std::string::npos); | |
| TSlotDescriptor tdesc2 = create_basic_slot_descriptor(2, "virtual_col"); | |
| TExpr virtual_expr = create_virtual_column_expr(TExprNodeType::FUNCTION_CALL); | |
| tdesc2.__set_virtual_column_expr(virtual_expr); | |
| SlotDescriptor slot_desc2(tdesc2); | |
| std::string debug_str2 = slot_desc2.debug_string(); | |
| EXPECT_TRUE(debug_str2.find("virtual_col") != std::string::npos); | |
| EXPECT_TRUE(debug_str2.find("is_virtual=true") != std::string::npos); | |
| } | |
| TEST(TupleDescriptorTest, GetColumnId) { | |
| ObjectPool pool; | |
| DescriptorTblBuilder builder(&pool); | |
| builder.declare_tuple() << std::make_shared<DataTypeInt32>() | |
| << std::make_shared<DataTypeInt64>(); | |
| auto* desc_tbl = builder.build(); | |
| auto* tuple_desc = desc_tbl->get_tuple_descriptor(0); | |
| ASSERT_EQ(tuple_desc->slots().size(), 2); | |
| EXPECT_EQ(tuple_desc->get_column_id(tuple_desc->slots()[0]->id()), 0); | |
| EXPECT_EQ(tuple_desc->get_column_id(tuple_desc->slots()[1]->id()), 1); | |
| EXPECT_EQ(tuple_desc->get_column_id(100), -1); | |
| } | |
| TEST(RowDescriptorTest, MultiTupleLayoutAndSingleTupleCount) { | |
| ObjectPool pool; | |
| DescriptorTblBuilder builder(&pool); | |
| builder.declare_tuple() << std::make_shared<DataTypeInt32>(); | |
| builder.declare_tuple() << std::make_shared<DataTypeInt64>() | |
| << std::make_shared<DataTypeInt32>(); | |
| auto* desc_tbl = builder.build(); | |
| RowDescriptor single(desc_tbl->get_tuple_descriptor(1)); | |
| EXPECT_EQ(single.num_slots(), 2); | |
| RowDescriptor row(*desc_tbl, {0, 1}); | |
| EXPECT_EQ(row.num_slots(), 3); | |
| ASSERT_EQ(row.tuple_descriptors().size(), 2); | |
| EXPECT_EQ(row.tuple_descriptors()[0], desc_tbl->get_tuple_descriptor(0)); | |
| EXPECT_EQ(row.tuple_descriptors()[1], desc_tbl->get_tuple_descriptor(1)); | |
| EXPECT_EQ(row.get_column_id(desc_tbl->get_tuple_descriptor(1)->slots()[0]->id()), 1); | |
| EXPECT_EQ(row.get_column_id(desc_tbl->get_tuple_descriptor(1)->slots()[1]->id()), 2); | |
| RowDescriptor copied(row); | |
| EXPECT_EQ(copied.num_slots(), row.num_slots()); | |
| EXPECT_EQ(copied.tuple_descriptors(), row.tuple_descriptors()); | |
| } | |
| TEST_F(SlotDescriptorTest, AccessPathsPreservedThroughProtobuf) { | |
| TColumnAccessPath data_path; | |
| data_path.__set_version(g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_TYPED); | |
| data_path.__set_type(TAccessPathType::DATA); | |
| TDataAccessPath data_payload; | |
| data_payload.__set_path({"s", "field"}); | |
| data_path.__set_data_access_path(data_payload); | |
| TColumnAccessPath meta_path; | |
| meta_path.__set_version(g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_TYPED); | |
| meta_path.__set_type(TAccessPathType::META); | |
| TMetaAccessPath meta_payload; | |
| meta_payload.__set_path({"s", "field", "NULL"}); | |
| meta_path.__set_meta_access_path(meta_payload); | |
| TColumnAccessPath legacy_path; | |
| legacy_path.__set_type(TAccessPathType::DATA); | |
| data_payload.__set_path({"s", "legacy", "NULL"}); | |
| legacy_path.__set_data_access_path(data_payload); | |
| TColumnAccessPath explicit_legacy_path; | |
| explicit_legacy_path.__set_version(g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_LEGACY); | |
| explicit_legacy_path.__set_type(TAccessPathType::DATA); | |
| data_payload.__set_path({"s", "legacy", "VALUES"}); | |
| explicit_legacy_path.__set_data_access_path(data_payload); | |
| TSlotDescriptor thrift_descriptor = create_basic_slot_descriptor(); | |
| thrift_descriptor.__set_all_access_paths( | |
| {data_path, meta_path, legacy_path, explicit_legacy_path}); | |
| thrift_descriptor.__set_predicate_access_paths({meta_path}); | |
| SlotDescriptor original(thrift_descriptor); | |
| PSlotDescriptor protobuf_descriptor; | |
| original.to_protobuf(&protobuf_descriptor); | |
| ASSERT_EQ(protobuf_descriptor.all_access_paths_size(), 4); | |
| EXPECT_TRUE(protobuf_descriptor.all_access_paths(0).has_version()); | |
| EXPECT_EQ(protobuf_descriptor.all_access_paths(0).version(), | |
| g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_TYPED); | |
| EXPECT_TRUE(protobuf_descriptor.all_access_paths(1).has_version()); | |
| EXPECT_FALSE(protobuf_descriptor.all_access_paths(2).has_version()); | |
| EXPECT_TRUE(protobuf_descriptor.all_access_paths(3).has_version()); | |
| EXPECT_EQ(protobuf_descriptor.all_access_paths(3).version(), | |
| g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_LEGACY); | |
| SlotDescriptor round_trip(protobuf_descriptor); | |
| EXPECT_EQ(round_trip.all_access_paths(), thrift_descriptor.all_access_paths); | |
| EXPECT_EQ(round_trip.predicate_access_paths(), thrift_descriptor.predicate_access_paths); | |
| EXPECT_FALSE(round_trip.all_access_paths()[1].__isset.data_access_path); | |
| EXPECT_FALSE(round_trip.predicate_access_paths()[0].__isset.data_access_path); | |
| EXPECT_FALSE(round_trip.all_access_paths()[2].__isset.version); | |
| EXPECT_TRUE(round_trip.all_access_paths()[3].__isset.version); | |
| EXPECT_EQ(round_trip.all_access_paths()[3].version, | |
| g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_LEGACY); | |
| } | |
| } // namespace doris |