@@ -240,6 +240,11 @@ TEST_P(BTreeGlobalIndexIntegrationTest, WriteAndReadIntData) {
240240 Literal literal_5 (5 );
241241 ASSERT_OK_AND_ASSIGN (result, reader->VisitGreaterThan (literal_5));
242242 CheckResult (result, {});
243+
244+ // GreaterThan 1 (min_key) -> skip entries with value==1 via from_inclusive=false
245+ Literal literal_1 (1 );
246+ ASSERT_OK_AND_ASSIGN (result, reader->VisitGreaterThan (literal_1));
247+ CheckResult (result, {3 , 4 , 6 , 7 , 8 , 9 , 10 });
243248 }
244249
245250 // --- VisitGreaterOrEqual ---
@@ -361,6 +366,10 @@ TEST_P(BTreeGlobalIndexIntegrationTest, WriteAndReadStringData) {
361366 Literal lit_cherry (FieldType::STRING , " cherry" , 6 );
362367 ASSERT_OK_AND_ASSIGN (auto result, reader->VisitGreaterThan (lit_cherry));
363368 CheckResult (result, {8 });
369+
370+ Literal lit_apple (FieldType::STRING , " apple" , 5 );
371+ ASSERT_OK_AND_ASSIGN (result, reader->VisitGreaterThan (lit_apple));
372+ CheckResult (result, {1 , 3 , 4 , 6 , 7 , 8 });
364373 }
365374
366375 // --- VisitGreaterOrEqual ---
@@ -402,6 +411,17 @@ TEST_P(BTreeGlobalIndexIntegrationTest, WriteAndReadStringData) {
402411 Literal lit_z (FieldType::STRING , " z" , 1 );
403412 ASSERT_OK_AND_ASSIGN (result, reader->VisitStartsWith (lit_z));
404413 CheckResult (result, {});
414+
415+ // StartsWith "" (empty prefix) -> all non-null rows
416+ Literal lit_empty (FieldType::STRING , " " , 0 );
417+ ASSERT_OK_AND_ASSIGN (result, reader->VisitStartsWith (lit_empty));
418+ CheckResult (result, {0 , 1 , 3 , 4 , 6 , 7 , 8 });
419+
420+ // StartsWith "\xFF\xFF" (all 0xFF bytes, overflow branch) -> no match
421+ std::string xff_prefix = " \xFF\xFF " ;
422+ Literal lit_xff (FieldType::STRING , xff_prefix.data (), xff_prefix.size ());
423+ ASSERT_OK_AND_ASSIGN (result, reader->VisitStartsWith (lit_xff));
424+ CheckResult (result, {});
405425 }
406426
407427 // --- VisitEndsWith (falls back to AllNonNullRows) ---
@@ -1448,6 +1468,27 @@ TEST_P(BTreeGlobalIndexIntegrationTest, WriteAndReadAllNull) {
14481468 ASSERT_OK_AND_ASSIGN (auto result, reader->VisitNotIn (not_in_literals));
14491469 CheckResult (result, {});
14501470 }
1471+
1472+ // --- VisitGreaterThan -> empty (min_key_/max_key_ are nullopt) ---
1473+ {
1474+ Literal lit_1 (1 );
1475+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitGreaterThan (lit_1));
1476+ CheckResult (result, {});
1477+ }
1478+
1479+ // --- VisitGreaterOrEqual -> empty ---
1480+ {
1481+ Literal lit_1 (1 );
1482+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitGreaterOrEqual (lit_1));
1483+ CheckResult (result, {});
1484+ }
1485+
1486+ // --- VisitLessOrEqual -> empty ---
1487+ {
1488+ Literal lit_1 (1 );
1489+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitLessOrEqual (lit_1));
1490+ CheckResult (result, {});
1491+ }
14511492}
14521493
14531494TEST_P (BTreeGlobalIndexIntegrationTest, WriteAndReadLargeDataWithSmallBlocks) {
@@ -1988,6 +2029,101 @@ TEST_P(BTreeGlobalIndexIntegrationTest, WriteAndReadMultiFilesWithMetaSelector)
19882029 }
19892030}
19902031
2032+ // Test both-bounds RangeQuery branch.
2033+ TEST_P (BTreeGlobalIndexIntegrationTest, TestRangeQuery) {
2034+ auto file_writer = std::make_shared<FakeGlobalIndexFileWriter>(fs_, base_path_);
2035+ auto field = arrow::field (" str_field" , arrow::utf8 ());
2036+ auto c_schema = CreateArrowSchema (field);
2037+
2038+ // Use very small block size to force data into multiple blocks
2039+ std::map<std::string, std::string> options = {{BtreeDefs::kBtreeIndexBlockSize , " 64" },
2040+ {BtreeDefs::kBtreeIndexCompression , GetParam ()}};
2041+ ASSERT_OK_AND_ASSIGN (auto indexer, BTreeGlobalIndexer::Create (options));
2042+ ASSERT_OK_AND_ASSIGN (auto writer,
2043+ indexer->CreateWriter (" str_field" , c_schema.get (), file_writer, pool_));
2044+ // Data layout
2045+ // 0->"aaa", 1->"aab", 2->"bba", 3->"bbb", 4->"bbc",
2046+ // 5->null, 6->"cca", 7->"ccb", 8->"ddd"
2047+ auto array = arrow::ipc::internal::json::ArrayFromJSON (arrow::struct_ ({field}), R"( [
2048+ ["aaa"],
2049+ ["aab"],
2050+ ["bba"],
2051+ ["bbb"],
2052+ ["bbc"],
2053+ [null],
2054+ ["cca"],
2055+ ["ccb"],
2056+ ["ddd"]
2057+ ])" )
2058+ .ValueOrDie ();
2059+
2060+ ArrowArray c_array;
2061+ ASSERT_TRUE (arrow::ExportArray (*array, &c_array).ok ());
2062+ std::vector<int64_t > row_ids (array->length ());
2063+ std::iota (row_ids.begin (), row_ids.end (), 0 );
2064+ ASSERT_OK (writer->AddBatch (&c_array, std::move (row_ids)));
2065+ ASSERT_OK_AND_ASSIGN (auto metas, writer->Finish ());
2066+ ASSERT_EQ (metas.size (), 1 );
2067+
2068+ auto file_reader = std::make_shared<FakeGlobalIndexFileReader>(fs_, base_path_);
2069+ c_schema = CreateArrowSchema (field);
2070+ ASSERT_OK_AND_ASSIGN (auto reader,
2071+ indexer->CreateReader (c_schema.get (), file_reader, metas, pool_));
2072+
2073+ // Non-null rows: {0,1,2,3,4,6,7,8}, Null rows: {5}
2074+
2075+ {
2076+ Literal lit_bb (FieldType::STRING , " bb" , 2 );
2077+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitStartsWith (lit_bb));
2078+ CheckResult (result, {2 , 3 , 4 });
2079+ }
2080+
2081+ {
2082+ Literal lit_cc (FieldType::STRING , " cc" , 2 );
2083+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitStartsWith (lit_cc));
2084+ CheckResult (result, {6 , 7 });
2085+ }
2086+
2087+ {
2088+ Literal lit_aaa (FieldType::STRING , " aaa" , 3 );
2089+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitGreaterThan (lit_aaa));
2090+ CheckResult (result, {1 , 2 , 3 , 4 , 6 , 7 , 8 });
2091+ }
2092+
2093+ {
2094+ Literal lit_ddd (FieldType::STRING , " ddd" , 3 );
2095+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitGreaterThan (lit_ddd));
2096+ CheckResult (result, {});
2097+ }
2098+
2099+ {
2100+ Literal lit_empty (FieldType::STRING , " " , 0 );
2101+ ASSERT_OK_AND_ASSIGN (auto result, reader->VisitStartsWith (lit_empty));
2102+ CheckResult (result, {0 , 1 , 2 , 3 , 4 , 6 , 7 , 8 });
2103+ }
2104+
2105+ // Raw RangeQuery call to cover both-bounds branch with from_inclusive=false and
2106+ // to_inclusive=false
2107+ {
2108+ ASSERT_FALSE (reader->IsThreadSafe ());
2109+ ASSERT_EQ (reader->GetIndexType (), BtreeDefs::kIdentifier );
2110+ auto lazy_reader = std::dynamic_pointer_cast<LazyFilteredBTreeReader>(reader);
2111+ ASSERT_TRUE (lazy_reader);
2112+ ASSERT_OK_AND_ASSIGN (auto tmp_reader, lazy_reader->GetOrCreateReader (metas[0 ]));
2113+ auto btree_reader = std::dynamic_pointer_cast<BTreeGlobalIndexReader>(tmp_reader);
2114+ ASSERT_TRUE (btree_reader);
2115+ ASSERT_FALSE (btree_reader->IsThreadSafe ());
2116+ ASSERT_EQ (btree_reader->GetIndexType (), BtreeDefs::kIdentifier );
2117+
2118+ Literal from_lit (FieldType::STRING , " bba" , 3 );
2119+ Literal to_lit (FieldType::STRING , " bbc" , 3 );
2120+ ASSERT_OK_AND_ASSIGN (RoaringBitmap64 range_result,
2121+ btree_reader->RangeQuery (from_lit, to_lit, /* from_inclusive=*/ false ,
2122+ /* to_inclusive=*/ false ));
2123+ ASSERT_EQ (range_result, RoaringBitmap64::From ({3 }));
2124+ }
2125+ }
2126+
19912127INSTANTIATE_TEST_SUITE_P (Compression, BTreeGlobalIndexIntegrationTest,
19922128 ::testing::ValuesIn (std::vector<std::string>({" none" , " zstd" , " lz4" })));
19932129
0 commit comments