diff --git a/db/db_bloom_filter_test.cc b/db/db_bloom_filter_test.cc index 3d52a873715..6c57bf58bc5 100644 --- a/db/db_bloom_filter_test.cc +++ b/db/db_bloom_filter_test.cc @@ -4415,6 +4415,60 @@ TEST_F(DBBloomFilterTest, FixedWidthSegments) { EXPECT_EQ(0, TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA)); } +TEST_F(DBBloomFilterTest, SstQueryFilterKeySegmentRange) { + // Up to seven 1-byte segments, so that each key segment range selects a + // distinct substring of the key + struct OneByteSegmentsExtractor : public KeySegmentsExtractor { + const char* Name() const override { return "OneByteSegmentsExtractor"; } + void Extract(const Slice& key_or_bound, KeyKind /*kind*/, + Result* result) const override { + for (uint32_t i = 1; i <= key_or_bound.size() && i <= 7; ++i) { + result->segment_ends.push_back(i); + } + } + }; + auto extractor = std::make_shared(); + + // All the key segment ranges supported by the filter serialization, which + // stores the selected range in the filter and decodes it at query time + const std::vector> ranges = { + {0, 1}, {0, 2}, {1, 2}, {0, 3}, {1, 3}, {2, 3}, {0, 4}, {1, 4}, + {2, 4}, {3, 4}, {0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {0, 6}}; + + using Keys = std::vector; + for (const auto& [from, to] : ranges) { + SCOPED_TRACE("SelectKeySegmentRange(" + std::to_string(from) + ", " + + std::to_string(to) + ")"); + auto filter = MakeSharedBytewiseMinMaxSQFC( + experimental::SelectKeySegmentRange(from, to)); + SstQueryFilterConfigs configs = {{filter}, extractor}; + SstQueryFilterConfigsManager::Data data = {{42, {{"foo", configs}}}}; + std::shared_ptr configs_manager; + ASSERT_OK(SstQueryFilterConfigsManager::MakeShared(data, &configs_manager)); + std::shared_ptr f; + ASSERT_OK(configs_manager->MakeSharedFactory("foo", 42, &f)); + + Options options = CurrentOptions(); + options.statistics = CreateDBStatistics(); + options.table_properties_collector_factories.push_back(f); + DestroyAndReopen(options); + + ASSERT_OK(Put("0000000", "val0")); + ASSERT_OK(Put("1111111", "val1")); + ASSERT_OK(Flush()); + + // Found: the selected segments of the bounds are within the min/max of + // the same segments in the file + EXPECT_EQ(RangeQueryKeys(*f, *db_, "1111111", "1111112"), + Keys({"1111111"})); + EXPECT_EQ(1, TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA)); + + // Filtered: the selected segments of the bounds are above the max + EXPECT_EQ(RangeQueryKeys(*f, *db_, "2222222", "2222223"), Keys({})); + EXPECT_EQ(0, TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA)); + } +} + } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) { diff --git a/db/experimental.cc b/db/experimental.cc index 71cef247b55..3ae8e40d23a 100644 --- a/db/experimental.cc +++ b/db/experimental.cc @@ -396,6 +396,7 @@ const char* DeserializeFilterInput(const char* p, const char* limit, *out = SelectKeySegment{BitwiseAnd(b, 0xf)}; return p; case 2: + b = BitwiseAnd(b, 0xf); // First 16 cases of SelectKeySegmentRange // that are not a single key segment // 0: 0-1 diff --git a/unreleased_history/bug_fixes/sst_query_filter_key_segment_range.md b/unreleased_history/bug_fixes/sst_query_filter_key_segment_range.md new file mode 100644 index 00000000000..5877fbc8698 --- /dev/null +++ b/unreleased_history/bug_fixes/sst_query_filter_key_segment_range.md @@ -0,0 +1 @@ +Fixed experimental SST query filters (`SstQueryFilterConfigs`) with `SelectKeySegmentRange` reading back every key segment range except segments 0 to 6 as segments 0 to 6. Range queries using `GetTableFilterForRangeQuery()` could then skip SST files that contain keys in the range, so the query silently returned too few keys.