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

jiangtian 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 cb595432 fix(cpp): skip missing device/measurement in tree queryByRow 
(#777)
cb595432 is described below

commit cb59543280364df451660400a80498f12667953e
Author: Hongzhi Gao <[email protected]>
AuthorDate: Mon Apr 13 09:44:04 2026 +0800

    fix(cpp): skip missing device/measurement in tree queryByRow (#777)
    
    Align QDSWithoutTimeGenerator with Java: treat E_DEVICE_NOT_EXIST and
    E_MEASUREMENT_NOT_EXIST as skippable paths and fix column index mapping
    when some paths are omitted. Add C++ and Python regression tests.
    
    Made-with: Cursor
---
 cpp/src/reader/qds_without_timegenerator.cc        | 22 +++++++-----
 .../tree_view/tsfile_tree_query_by_row_test.cc     | 38 ++++++++++++++++++++
 python/tests/test_query_by_row.py                  | 40 ++++++++++++++++++++++
 3 files changed, 91 insertions(+), 9 deletions(-)

diff --git a/cpp/src/reader/qds_without_timegenerator.cc 
b/cpp/src/reader/qds_without_timegenerator.cc
index 0710b587..b895f2e3 100644
--- a/cpp/src/reader/qds_without_timegenerator.cc
+++ b/cpp/src/reader/qds_without_timegenerator.cc
@@ -19,6 +19,7 @@
 
 #include "qds_without_timegenerator.h"
 
+#include "utils/errno_define.h"
 #include "utils/util_define.h"
 
 using namespace common;
@@ -66,17 +67,20 @@ int QDSWithoutTimeGenerator::init_internal(TsFileIOReader* 
io_reader,
         TsFileSeriesScanIterator* ssi = nullptr;
         ret = io_reader_->alloc_ssi(paths[i].device_id_, paths[i].measurement_,
                                     ssi, pa_, global_time_filter);
-        if (ret != 0) {
+        if (ret == E_MEASUREMENT_NOT_EXIST || ret == E_DEVICE_NOT_EXIST) {
+            continue;
+        }
+        if (ret != E_OK) {
             return ret;
-        } else {
-            index_lookup_.insert({paths[i].measurement_, i + 1});
-            if (paths[i].full_path_ != paths[i].measurement_) {
-                index_lookup_.insert({paths[i].full_path_, i + 1});
-            }
-            ssi_vec_.push_back(ssi);
-            valid_paths.push_back(paths[i]);
-            column_names.push_back(paths[i].full_path_);
         }
+        size_t col_idx = ssi_vec_.size();
+        index_lookup_.insert({paths[i].measurement_, col_idx + 1});
+        if (paths[i].full_path_ != paths[i].measurement_) {
+            index_lookup_.insert({paths[i].full_path_, col_idx + 1});
+        }
+        ssi_vec_.push_back(ssi);
+        valid_paths.push_back(paths[i]);
+        column_names.push_back(paths[i].full_path_);
     }
 
     size_t path_count = valid_paths.size();
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 74845e44..1e958253 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
@@ -133,6 +133,44 @@ TEST_F(TreeQueryByRowTest, NoOffsetNoLimit) {
     reader.close();
 }
 
+// queryByRow skips paths whose device or measurement is missing in the file;
+// only existing series are returned (aligned with Java tree reader).
+TEST_F(TreeQueryByRowTest, QueryByRow_SkipsMissingDeviceAndMeasurement) {
+    std::vector<std::string> devices = {"d1"};
+    std::vector<std::string> measurements = {"s1"};
+    const int num_rows = 5;
+    write_test_file(devices, measurements, num_rows);
+
+    TsFileTreeReader reader;
+    ASSERT_EQ(E_OK, reader.open(file_name_));
+
+    ResultSet* result = nullptr;
+    std::vector<std::string> q_devices = {"d1", "d999"};
+    std::vector<std::string> q_meas = {"s1", "ghost_m"};
+    ASSERT_EQ(E_OK, reader.queryByRow(q_devices, q_meas, 0, -1, result));
+    ASSERT_NE(result, nullptr);
+
+    auto meta = result->get_metadata();
+    ASSERT_EQ(2u, meta->get_column_count());
+
+    bool has_next = false;
+    int row_count = 0;
+    while (IS_SUCC(result->next(has_next)) && has_next) {
+        RowRecord* rr = result->get_row_record();
+        int64_t ts = rr->get_timestamp();
+        ASSERT_EQ(ts, static_cast<int64_t>(row_count));
+        Field* f = rr->get_field(1);
+        ASSERT_NE(f, nullptr);
+        ASSERT_EQ(f->type_, INT64);
+        EXPECT_EQ(f->get_value<int64_t>(), static_cast<int64_t>(ts * 100 + 0));
+        row_count++;
+    }
+    EXPECT_EQ(row_count, num_rows);
+
+    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 e45cd1b2..3adae996 100644
--- a/python/tests/test_query_by_row.py
+++ b/python/tests/test_query_by_row.py
@@ -110,3 +110,43 @@ def test_query_table_by_row_offset_limit():
         if os.path.exists(file_path):
             os.remove(file_path)
 
+
+def test_query_tree_by_row_skips_missing_device_and_measurement():
+    """Tree queryByRow: missing device or measurement paths are skipped 
(Java-aligned)."""
+    file_path = "python_tree_query_by_row_skip_missing.tsfile"
+    if os.path.exists(file_path):
+        os.remove(file_path)
+
+    try:
+        device_ids = ["d1"]
+        measurement_names = ["s1"]
+        num_rows = 5
+
+        writer = TsFileWriter(file_path)
+        for device_id in device_ids:
+            for measurement in measurement_names:
+                writer.register_timeseries(device_id, 
TimeseriesSchema(measurement, TSDataType.INT64))
+
+        for t in range(num_rows):
+            fields = [Field("s1", t * 100 + 0, TSDataType.INT64)]
+            writer.write_row_record(RowRecord(device_ids[0], t, fields))
+
+        writer.close()
+
+        reader = TsFileReader(file_path)
+        q_devices = ["d1", "d999"]
+        q_measurements = ["s1", "ghost_m"]
+        with reader.query_tree_by_row(q_devices, q_measurements, 0, -1) as 
result:
+            assert result.get_metadata().get_column_num() == 2
+            row = 0
+            while result.next():
+                ts = result.get_value_by_index(1)
+                assert ts == row
+                assert result.get_value_by_index(2) == row * 100 + 0
+                row += 1
+            assert row == num_rows
+        reader.close()
+    finally:
+        if os.path.exists(file_path):
+            os.remove(file_path)
+

Reply via email to