diff --git a/.github/workflows/unit-test-cpp.yml b/.github/workflows/unit-test-cpp.yml index 7a0605c8b..3e46d6920 100644 --- a/.github/workflows/unit-test-cpp.yml +++ b/.github/workflows/unit-test-cpp.yml @@ -80,6 +80,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v7 + - name: Set up Python for clang-format + if: runner.os == 'Linux' + uses: actions/setup-python@v7 + with: + python-version: "3.11" + - name: Set up JDK 17 uses: actions/setup-java@v6.0.1 with: @@ -121,10 +127,10 @@ jobs: elif command -v yum >/dev/null 2>&1; then sudo yum install -y libuuid-devel fi - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 - sudo update-alternatives --set clang-format /usr/bin/clang-format-17 - sudo apt-get update - sudo apt-get install -y uuid-dev + python -m venv "$RUNNER_TEMP/clang-format" + "$RUNNER_TEMP/clang-format/bin/python" -m pip install --only-binary=:all: clang-format==17.0.6 + "$RUNNER_TEMP/clang-format/bin/clang-format" --version + echo "$RUNNER_TEMP/clang-format/bin" >> "$GITHUB_PATH" elif [[ "$RUNNER_OS" == "Windows" ]]; then choco install llvm --version 17.0.6 --force else diff --git a/.github/workflows/unit-test-python.yml b/.github/workflows/unit-test-python.yml index b01c09341..854774034 100644 --- a/.github/workflows/unit-test-python.yml +++ b/.github/workflows/unit-test-python.yml @@ -45,6 +45,13 @@ jobs: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] python-version: ["3.9", "3.11", "3.14"] + exclude: + - os: ubuntu-latest + python-version: "3.9" + include: + # Python 3.9 has no setup-python build for Ubuntu 26.04. + - os: ubuntu-22.04 + python-version: "3.9" runs-on: ${{ matrix.os }} steps: @@ -92,8 +99,10 @@ jobs: shell: bash run: | if [[ "$RUNNER_OS" == "Linux" ]]; then - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 100 - sudo update-alternatives --set clang-format /usr/bin/clang-format-17 + python -m venv "$RUNNER_TEMP/clang-format" + "$RUNNER_TEMP/clang-format/bin/python" -m pip install --only-binary=:all: clang-format==17.0.6 + "$RUNNER_TEMP/clang-format/bin/clang-format" --version + echo "$RUNNER_TEMP/clang-format/bin" >> "$GITHUB_PATH" sudo apt-get update sudo apt-get install -y uuid-dev elif [[ "$RUNNER_OS" == "Windows" ]]; then diff --git a/cpp/src/common/schema.h b/cpp/src/common/schema.h index ed59b27b1..feb2eb799 100644 --- a/cpp/src/common/schema.h +++ b/cpp/src/common/schema.h @@ -254,10 +254,14 @@ class TableSchema { } } + // Keep this explicit for the supported MSVC v141 toolset. TableSchema(TableSchema&& other) noexcept : table_name_(std::move(other.table_name_)), column_schemas_(std::move(other.column_schemas_)), column_categories_(std::move(other.column_categories_)), + column_pos_index_(std::move(other.column_pos_index_)), + is_virtual_table_(other.is_virtual_table_), + max_level_(other.max_level_), updatable_(other.updatable_) {} TableSchema(const TableSchema& other) noexcept diff --git a/cpp/test/common/schema_test.cc b/cpp/test/common/schema_test.cc index 1c908ab51..4308bc4a9 100644 --- a/cpp/test/common/schema_test.cc +++ b/cpp/test/common/schema_test.cc @@ -71,6 +71,63 @@ TEST(MeasurementSchemaGroupTest, DefaultConstructor) { EXPECT_FALSE(group.is_aligned_); } +TEST(TableSchemaTest, MovePreservesColumnIndex) { + TableSchema original( + "Table", + {new MeasurementSchema("Value", common::INT64, common::PLAIN, + common::UNCOMPRESSED)}, + {common::ColumnCategory::FIELD}); + auto measurement = original.get_measurement_schemas().front(); + TableSchema moved(std::move(original)); + + EXPECT_EQ(moved.get_table_name(), "table"); + ASSERT_EQ(moved.get_columns_num(), 1); + EXPECT_EQ(moved.get_column_pos_index_num(), 1); + EXPECT_EQ(moved.get_measurement_schemas().front(), measurement); + EXPECT_EQ(moved.find_column_index("VALUE"), 0); + EXPECT_EQ(moved.get_data_types(), + std::vector({common::INT64})); + EXPECT_EQ( + moved.get_column_categories(), + std::vector({common::ColumnCategory::FIELD})); +} + +TEST(TableSchemaTest, MovePreservesVirtualTableCaseSensitivity) { + TableSchema original( + "Tree", + {common::ColumnSchema("Device", common::STRING, common::UNCOMPRESSED, + common::PLAIN, common::ColumnCategory::TAG)}, + true); + TableSchema moved(std::move(original)); + + EXPECT_TRUE(moved.is_virtual_table()); + EXPECT_EQ(moved.get_table_name(), "Tree"); + EXPECT_EQ(moved.find_column_index("Device"), 0); + EXPECT_EQ(moved.find_column_index("device"), -1); + EXPECT_EQ(moved.find_id_column_order("Device"), 0); + EXPECT_EQ(moved.find_id_column_order("device"), -1); +} + +TEST(TableSchemaTest, MovePreservesPendingTreeLevels) { + common::PageArena arena; + ChunkGroupMeta metadata(&arena); + auto device = std::make_shared( + std::vector{"root", "device", "sensor"}); + ASSERT_EQ(device->segment_num(), 3); + ASSERT_EQ(metadata.init(device), common::E_OK); + TableSchema original; + original.update(&metadata); + TableSchema moved(std::move(original)); + moved.finalize_column_schema(); + + EXPECT_EQ(moved.get_measurement_names(), + std::vector({"__level1", "__level2"})); + EXPECT_EQ( + moved.get_column_categories(), + std::vector(2, common::ColumnCategory::TAG)); + EXPECT_EQ(moved.find_id_column_order("__level2"), 1); +} + #if DEBUG_SE TEST(TableSchemaTest, BasicTest) { using namespace storage; diff --git a/cpp/test/writer/table_view/tsfile_writer_table_test.cc b/cpp/test/writer/table_view/tsfile_writer_table_test.cc index 9e716558f..eea31bda1 100644 --- a/cpp/test/writer/table_view/tsfile_writer_table_test.cc +++ b/cpp/test/writer/table_view/tsfile_writer_table_test.cc @@ -133,6 +133,51 @@ class TsFileWriterTableTest : public ::testing::Test { } }; +TEST_F(TsFileWriterTableTest, RegisterMovedSchemaAndReadWrittenValue) { + TableSchema original( + "moved", {new MeasurementSchema("value", INT64, PLAIN, UNCOMPRESSED)}, + {ColumnCategory::FIELD}); + auto schema = std::make_shared(std::move(original)); + TsFileWriter writer; + ASSERT_EQ(writer.init(&write_file_), E_OK); + ASSERT_EQ(writer.register_table(schema), E_OK); + + Tablet tablet(schema->get_measurement_names(), schema->get_data_types(), 1); + tablet.set_table_name("moved"); + ASSERT_EQ(tablet.add_timestamp(0, 17), E_OK); + ASSERT_EQ(tablet.add_value(0, "value", int64_t(42)), E_OK); + ASSERT_EQ(writer.write_table(tablet), E_OK); + ASSERT_EQ(writer.close(), E_OK); + + TsFileReader reader; + ASSERT_EQ(reader.open(file_name_), E_OK); + ResultSet* result = nullptr; + ASSERT_EQ(reader.query("moved", {"value"}, 0, INT64_MAX, result), E_OK); + ASSERT_NE(result, nullptr); + bool has_next = false; + ASSERT_EQ(result->next(has_next), E_OK); + ASSERT_TRUE(has_next); + EXPECT_EQ(result->get_value("time"), 17); + EXPECT_EQ(result->get_value("value"), 42); + ASSERT_EQ(result->next(has_next), E_OK); + EXPECT_FALSE(has_next); + reader.destroy_query_data_set(result); + ASSERT_EQ(reader.close(), E_OK); +} + +TEST_F(TsFileWriterTableTest, MovedSchemaStillRejectsDuplicateColumns) { + TableSchema original( + "duplicates", + {new MeasurementSchema("Value", INT64, PLAIN, UNCOMPRESSED), + new MeasurementSchema("value", INT64, PLAIN, UNCOMPRESSED)}, + {ColumnCategory::FIELD, ColumnCategory::FIELD}); + auto schema = std::make_shared(std::move(original)); + TsFileWriter writer; + ASSERT_EQ(writer.init(&write_file_), E_OK); + EXPECT_EQ(writer.register_table(schema), E_INVALID_ARG); + EXPECT_EQ(writer.close(), E_OK); +} + TEST_F(TsFileWriterTableTest, WriteTableTest) { auto table_schema = gen_table_schema(0); auto tsfile_table_writer_ =