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):