Summary: Similar to v2 (db and table code understands prefixes), but use ReadOptions as in v3. Also, make the CreateFilter code faster and cleaner. Test Plan: make db_test; export LEVELDB_TESTS=PrefixScan; ./db_test Reviewers: dhruba Reviewed By: dhruba CC: haobo, emayanke Differential Revision: https://reviews.facebook.net/D12027main
parent
3b81df34bd
commit
f5f1842282
@ -0,0 +1,76 @@ |
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
//
|
||||
// Wrap an underlying iterator, but exclude any results not starting
|
||||
// with a given prefix. Seeking to keys not beginning with the prefix
|
||||
// is invalid, and SeekToLast is not implemented (that would be
|
||||
// non-trivial), but otherwise this iterator will behave just like the
|
||||
// underlying iterator would if there happened to be no non-matching
|
||||
// keys in the dataset.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_DB_PREFIX_FILTER_ITERATOR_H_ |
||||
#define STORAGE_LEVELDB_DB_PREFIX_FILTER_ITERATOR_H_ |
||||
|
||||
#include "leveldb/iterator.h" |
||||
|
||||
namespace leveldb { |
||||
|
||||
class PrefixFilterIterator : public Iterator { |
||||
private: |
||||
Iterator* iter_; |
||||
const Slice &prefix_; |
||||
const SliceTransform *prefix_extractor_; |
||||
Status status_; |
||||
|
||||
public: |
||||
PrefixFilterIterator(Iterator* iter, |
||||
const Slice &prefix, |
||||
const SliceTransform* prefix_extractor) |
||||
: iter_(iter), prefix_(prefix), |
||||
prefix_extractor_(prefix_extractor), |
||||
status_(Status::OK()) { |
||||
if (prefix_extractor == nullptr) { |
||||
status_ = Status::InvalidArgument("A prefix filter may not be used " |
||||
"unless a function is also defined " |
||||
"for extracting prefixes"); |
||||
} else if (!prefix_extractor_->InRange(prefix)) { |
||||
status_ = Status::InvalidArgument("Must provide a slice for prefix which" |
||||
"is a prefix for some key"); |
||||
} |
||||
} |
||||
~PrefixFilterIterator() { |
||||
delete iter_; |
||||
} |
||||
Slice key() const { return iter_->key(); } |
||||
Slice value() const { return iter_->value(); } |
||||
Status status() const { |
||||
if (!status_.ok()) { |
||||
return status_; |
||||
} |
||||
return iter_->status(); |
||||
} |
||||
void Next() { iter_->Next(); } |
||||
void Prev() { iter_->Prev(); } |
||||
void Seek(const Slice& k) { |
||||
if (prefix_extractor_->Transform(k) == prefix_) { |
||||
iter_->Seek(k); |
||||
} else { |
||||
status_ = Status::InvalidArgument("Seek must begin with target prefix"); |
||||
} |
||||
} |
||||
void SeekToFirst() { |
||||
Seek(prefix_); |
||||
} |
||||
void SeekToLast() { |
||||
status_ = Status::NotSupported("SeekToLast is incompatible with prefixes"); |
||||
} |
||||
bool Valid() const { |
||||
return (status_.ok() && iter_->Valid() && |
||||
prefix_extractor_->Transform(iter_->key()) == prefix_); |
||||
} |
||||
}; |
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif |
@ -0,0 +1,41 @@ |
||||
// Copyright (c) 2012 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
//
|
||||
// Class for specifying user-defined functions which perform a
|
||||
// transformation on a slice. It is not required that every slice
|
||||
// belong to the domain and/or range of a function. Subclasses should
|
||||
// define InDomain and InRange to determine which slices are in either
|
||||
// of these sets respectively.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_TRANSFORM_H_ |
||||
#define STORAGE_LEVELDB_INCLUDE_SLICE_TRANSFORM_H_ |
||||
|
||||
#include <string> |
||||
|
||||
namespace leveldb { |
||||
|
||||
class Slice; |
||||
|
||||
class SliceTransform { |
||||
public: |
||||
virtual ~SliceTransform() {}; |
||||
|
||||
// Return the name of this transformation.
|
||||
virtual const char* Name() const = 0; |
||||
|
||||
// transform a src in domain to a dst in the range
|
||||
virtual Slice Transform(const Slice& src) const = 0; |
||||
|
||||
// determine whether this is a valid src upon the function applies
|
||||
virtual bool InDomain(const Slice& src) const = 0; |
||||
|
||||
// determine whether dst=Transform(src) for some src
|
||||
virtual bool InRange(const Slice& dst) const = 0; |
||||
}; |
||||
|
||||
extern const SliceTransform* NewFixedPrefixTransform(size_t prefix_len); |
||||
|
||||
} |
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_SLICE_TRANSFORM_H_
|
@ -0,0 +1,43 @@ |
||||
// Copyright (c) 2012 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/slice_transform.h" |
||||
|
||||
#include "leveldb/slice.h" |
||||
|
||||
namespace leveldb { |
||||
|
||||
namespace { |
||||
|
||||
class FixedPrefixTransform : public SliceTransform { |
||||
private: |
||||
size_t prefix_len_; |
||||
|
||||
public: |
||||
explicit FixedPrefixTransform(size_t prefix_len) : prefix_len_(prefix_len) { } |
||||
|
||||
virtual const char* Name() const { |
||||
return "rocksdb.FixedPrefix"; |
||||
} |
||||
|
||||
virtual Slice Transform(const Slice& src) const { |
||||
assert(InDomain(src)); |
||||
return Slice(src.data(), prefix_len_); |
||||
} |
||||
|
||||
virtual bool InDomain(const Slice& src) const { |
||||
return (src.size() >= prefix_len_); |
||||
} |
||||
|
||||
virtual bool InRange(const Slice& dst) const { |
||||
return (dst.size() == prefix_len_); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
const SliceTransform* NewFixedPrefixTransform(size_t prefix_len) { |
||||
return new FixedPrefixTransform(prefix_len); |
||||
} |
||||
|
||||
} // namespace leveldb
|
Loading…
Reference in new issue