glean/rts/lookup.cpp (186 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #include "glean/rts/lookup.h" #include "glean/rts/error.h" namespace facebook { namespace glean { namespace rts { EmptyLookup& EmptyLookup::instance() { static EmptyLookup object; return object; } namespace { struct MergeIterator final : public FactIterator { MergeIterator( std::unique_ptr<FactIterator> l, std::unique_ptr<FactIterator> r, size_t pfxsize) : left(std::move(l)) , right(std::move(r)) , current(nullptr) , prefix_size(pfxsize) {} void next() override { if (!current) { get(KeyOnly); } current->next(); current = nullptr; } Fact::Ref get(Demand demand) override { if (current) { return current->get(demand); } else { auto l = tryGet(left, demand); auto r = tryGet(right, demand); if (l) { if (r && suffix(r) < suffix(l)) { current = right.get(); return r; } else { current = left.get(); return l; } } else { current = right.get(); return r; } } } static Fact::Ref tryGet(std::unique_ptr<FactIterator>& iter, Demand demand) { Fact::Ref ref; if (iter) { ref = iter->get(demand); if (!ref) { iter.reset(); } } return ref; } folly::ByteRange suffix(Fact::Ref ref) { assert(prefix_size <= ref.key().size()); return {ref.key().begin() + prefix_size, ref.key().end()}; } std::unique_ptr<FactIterator> left; std::unique_ptr<FactIterator> right; FactIterator * FOLLY_NULLABLE current; const size_t prefix_size; }; } std::unique_ptr<FactIterator> FactIterator::merge( std::unique_ptr<FactIterator> left, std::unique_ptr<FactIterator> right, size_t prefix_size) { if (left->get(Demand::KeyOnly)) { return right->get(Demand::KeyOnly) ? std::make_unique<MergeIterator>( std::move(left), std::move(right), prefix_size) : std::move(left); } else { return right; } } std::unique_ptr<FactIterator> Snapshot::enumerate(Id from, Id upto) { if (from >= boundary()) { return std::make_unique<EmptyIterator>(); } else { return base()->enumerate( from, upto ? std::min(upto, boundary()) : boundary()); } } std::unique_ptr<FactIterator> Snapshot::enumerateBack(Id from, Id downto) { if (downto >= boundary()) { return std::make_unique<EmptyIterator>(); } else { return base()->enumerate( from ? std::min(from, boundary()) : boundary(), downto); } } std::unique_ptr<FactIterator> Snapshot::seek( Pid type, folly::ByteRange start, size_t prefix_size) { struct Iterator final : FactIterator { Iterator(std::unique_ptr<FactIterator> base, Id boundary) : base_(std::move(base)), boundary_(boundary) {} void next() override { base_->next(); } Fact::Ref get(Demand demand) override { auto r = base_->get(demand); return r && r.id < boundary_ ? r : Fact::Ref::invalid(); } std::unique_ptr<FactIterator> base_; Id boundary_; }; return std::make_unique<Iterator>( base()->seek(type, start, prefix_size), boundary()); } namespace { struct AppendIterator final : FactIterator { AppendIterator( std::unique_ptr<FactIterator> l, std::unique_ptr<FactIterator> r) : current(std::move(l)) , other(std::move(r)) , checked(false) {} void next() override { if (!checked) { get(KeyOnly); } current->next(); checked = false; } Fact::Ref get(Demand demand) override { checked = true; auto r = current->get(); if (!r && other) { std::swap(current,other); other.reset(); r = current->get(); } return r; } std::unique_ptr<FactIterator> current; std::unique_ptr<FactIterator> other; bool checked; }; } std::unique_ptr<FactIterator> FactIterator::append( std::unique_ptr<FactIterator> left, std::unique_ptr<FactIterator> right) { return std::make_unique<AppendIterator>(std::move(left), std::move(right)); } namespace { struct FilterIterator final : FactIterator { FilterIterator( std::unique_ptr<FactIterator> base, std::function<bool(Id id)> visible) : base_(std::move(base)), visible_(std::move(visible)) {} void next() override { base_->next(); } Fact::Ref get(Demand demand) override { // TODO: If we're doing a prefix seek and demand requires values, this // will do 2 DB lookups (to get the value) even for facts that we // skip which can be pretty significant. One possibility to avoid // this is to do a KeyOnly lookup first, check the fact id and do // a KeyValue lookup (if necessary) only if we want the // fact. Another is to push the filtering all the way down into // the individual iterators (ugly). auto r = base_->get(demand); while (r.id != Id::invalid() && !visible_(r.id)) { base_->next(); r = base_->get(demand); } return r; } std::unique_ptr<FactIterator> base_; std::function<bool(Id id)> visible_; }; } // namespace std::unique_ptr<FactIterator> FactIterator::filter( std::unique_ptr<FactIterator> base, std::function<bool(Id id)> visible) { return std::make_unique<FilterIterator>( std::move(base), std::move(visible)); } } } }