diff --git a/cpp/src/common/path.cc b/cpp/src/common/path.cc index fae3bff26..d70a9d6c6 100644 --- a/cpp/src/common/path.cc +++ b/cpp/src/common/path.cc @@ -19,6 +19,8 @@ #include "common/path.h" +#include "common/constant/tsfile_constant.h" + #ifdef ENABLE_ANTLR4 #include "parser/path_nodes_generator.h" #endif @@ -47,8 +49,17 @@ Path::Path(const std::string& path_sc, bool if_split) { IDeviceID::split_string(path_sc, '.'); #endif if (nodes.size() > 1) { - device_id_ = std::make_shared( - std::vector(nodes.begin(), nodes.end() - 1)); + // Join nodes, then parse like write path / Java Path (not + // per-segment vector). + std::string device_joined; + for (size_t i = 0; i + 1 < nodes.size(); ++i) { + if (i > 0) { + device_joined += PATH_SEPARATOR_CHAR; + } + device_joined += nodes[i]; + } + device_id_ = + std::make_shared(device_joined); measurement_ = nodes[nodes.size() - 1]; full_path_ = device_id_->get_device_name() + "." + measurement_; } else { diff --git a/cpp/test/reader/tree_view/tsfile_tree_query_by_row_test.cc b/cpp/test/reader/tree_view/tsfile_tree_query_by_row_test.cc index 1e9582539..80bd974e1 100644 --- a/cpp/test/reader/tree_view/tsfile_tree_query_by_row_test.cc +++ b/cpp/test/reader/tree_view/tsfile_tree_query_by_row_test.cc @@ -171,6 +171,32 @@ TEST_F(TreeQueryByRowTest, QueryByRow_SkipsMissingDeviceAndMeasurement) { reader.close(); } +// Device id with three dot-separated parts (e.g. root.sg1.FeederA) must resolve +// to the same StringArrayDeviceID normalization as write path; queryByRow must +// not return E_DEVICE_NOT_EXIST. +TEST_F(TreeQueryByRowTest, QueryByRow_MultiSegmentDeviceId) { + std::vector devices = {"root.sg1.FeederA"}; + std::vector measurements = {"s1"}; + int num_rows = 10; + write_test_file(devices, measurements, num_rows); + + TsFileTreeReader reader; + ASSERT_EQ(E_OK, reader.open(file_name_)); + + ResultSet* result = nullptr; + ASSERT_EQ(E_OK, reader.queryByRow(devices, measurements, 0, 5, result)); + ASSERT_NE(result, nullptr); + + auto timestamps = collect_timestamps(result); + ASSERT_EQ(timestamps.size(), 5u); + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(timestamps[i], i); + } + + reader.destroy_query_data_set(result); + reader.close(); +} + // Test: offset skips leading rows. TEST_F(TreeQueryByRowTest, OffsetOnly) { std::vector devices = {"d1"}; diff --git a/python/tests/test_query_by_row.py b/python/tests/test_query_by_row.py index 3adae9965..fbe6a2c31 100644 --- a/python/tests/test_query_by_row.py +++ b/python/tests/test_query_by_row.py @@ -69,6 +69,42 @@ def test_query_tree_by_row_offset_limit(): os.remove(file_path) +def test_query_tree_by_row_multi_segment_device(): + file_path = "python_tree_query_by_row_multiseg_test.tsfile" + if os.path.exists(file_path): + os.remove(file_path) + + try: + device_id = "root.sg1.FeederA" + measurement_names = ["s1"] + num_rows = 10 + + writer = TsFileWriter(file_path) + for measurement in measurement_names: + writer.register_timeseries(device_id, TimeseriesSchema(measurement, TSDataType.INT64)) + + for t in range(num_rows): + fields = [Field(measurement_names[0], t * 100, TSDataType.INT64)] + writer.write_row_record(RowRecord(device_id, t, fields)) + + writer.close() + + reader = TsFileReader(file_path) + limit = 5 + with reader.query_tree_by_row([device_id], measurement_names, 0, limit) as result: + row = 0 + while result.next(): + ts = result.get_value_by_index(1) + assert ts == row + assert result.get_value_by_index(2) == ts * 100 + row += 1 + assert row == limit + reader.close() + finally: + if os.path.exists(file_path): + os.remove(file_path) + + def test_query_table_by_row_offset_limit(): file_path = "python_table_query_by_row_test.tsfile" if os.path.exists(file_path):