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

hongzhigao pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/tsfile.git


The following commit(s) were added to refs/heads/develop by this push:
     new 3c809d5f Fix tree queryByRow device ID parsing for multi-segment paths 
(#776)
3c809d5f is described below

commit 3c809d5f63aa64b2a96f6d21ceda53bad0a0d559
Author: Hongzhi Gao <[email protected]>
AuthorDate: Mon Apr 13 10:20:23 2026 +0800

    Fix tree queryByRow device ID parsing for multi-segment paths (#776)
    
    Path splitting now joins device nodes and parses via 
StringArrayDeviceID(string)
    to match write-time normalization. Add C++ and Python regression tests.
---
 cpp/src/common/path.cc                             | 15 +++++++--
 .../tree_view/tsfile_tree_query_by_row_test.cc     | 26 ++++++++++++++++
 python/tests/test_query_by_row.py                  | 36 ++++++++++++++++++++++
 3 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/cpp/src/common/path.cc b/cpp/src/common/path.cc
index fae3bff2..d70a9d6c 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<StringArrayDeviceID>(
-                    std::vector<std::string>(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<StringArrayDeviceID>(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 1e958253..80bd974e 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<std::string> devices = {"root.sg1.FeederA"};
+    std::vector<std::string> 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<std::string> devices = {"d1"};
diff --git a/python/tests/test_query_by_row.py 
b/python/tests/test_query_by_row.py
index 3adae996..fbe6a2c3 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):

Reply via email to