From 21696ba502d7b2191d6e9c984536f3cc1fc452dd Mon Sep 17 00:00:00 2001 From: Siying Dong Date: Fri, 28 Jul 2017 16:23:50 -0700 Subject: [PATCH] Replace dynamic_cast<> Summary: Replace dynamic_cast<> so that users can choose to build with RTTI off, so that they can save several bytes per object, and get tiny more memory available. Some nontrivial changes: 1. Add Comparator::GetRootComparator() to get around the internal comparator hack 2. Add the two experiemental functions to DB 3. Add TableFactory::GetOptionString() to avoid unnecessary casting to get the option string 4. Since 3 is done, move the parsing option functions for table factory to table factory files too, to be symmetric. Closes https://github.com/facebook/rocksdb/pull/2645 Differential Revision: D5502723 Pulled By: siying fbshipit-source-id: fd13cec5601cf68a554d87bfcf056f2ffa5fbf7c --- HISTORY.md | 1 + Makefile | 12 + cache/clock_cache.cc | 5 + db/convenience.cc | 6 +- db/db_impl.h | 6 +- db/dbformat.h | 3 + db/experimental.cc | 12 +- db/wal_manager.cc | 5 +- env/mock_env.cc | 4 +- examples/Makefile | 4 + examples/compaction_filter_example.cc | 6 +- include/rocksdb/comparator.h | 4 + include/rocksdb/db.h | 11 + include/rocksdb/table.h | 6 + include/rocksdb/utilities/stackable_db.h | 11 + monitoring/histogram.cc | 4 +- monitoring/histogram_windowing.cc | 5 +- options/options_helper.cc | 315 +----------------- options/options_helper.h | 112 +------ options/options_parser.cc | 59 +--- options/options_parser.h | 10 +- options/options_test.cc | 24 +- table/block_based_table_factory.cc | 193 ++++++++++- table/block_based_table_factory.h | 94 +++++- table/cuckoo_table_factory.h | 5 + table/plain_table_factory.cc | 144 +++++++- table/plain_table_factory.h | 32 ++ tools/db_bench_tool.cc | 6 +- tools/ldb_cmd.cc | 7 +- tools/sst_dump_tool.cc | 12 +- util/cast_util.h | 19 ++ utilities/blob_db/blob_db_impl.cc | 28 +- utilities/column_aware_encoding_util.cc | 4 +- utilities/options/options_util_test.cc | 26 +- .../optimistic_transaction_impl.cc | 12 +- utilities/transactions/transaction_db_impl.cc | 11 +- utilities/transactions/transaction_impl.cc | 11 +- .../transactions/transaction_lock_mgr.cc | 6 +- 38 files changed, 684 insertions(+), 551 deletions(-) create mode 100644 util/cast_util.h diff --git a/HISTORY.md b/HISTORY.md index 9d8468c6e..76e01f04c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,7 @@ ## Unreleased ### New Features * Add Iterator::Refresh(), which allows users to update the iterator state so that they can avoid some initialization costs of recreating iterators. +* Replace dynamic_cast<> (except unit test) so people can choose to build with RTTI off. With make, release mode is by default built with -fno-rtti and debug mode is built without it. Users can override it by setting USE_RTTI=0 or 1. ## 5.7.0 (07/13/2017) ### Public API Change diff --git a/Makefile b/Makefile index 55f642135..a01c74e95 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,19 @@ endif ifeq ($(DEBUG_LEVEL),0) OPT += -DNDEBUG DISABLE_WARNING_AS_ERROR=1 + +ifneq ($(USE_RTTI), 1) + CXXFLAGS += -fno-rtti +else + CXXFLAGS += -DROCKSDB_USE_RTTI +endif else +ifneq ($(USE_RTTI), 0) + CXXFLAGS += -DROCKSDB_USE_RTTI +else + CXXFLAGS += -fno-rtti +endif + $(warning Warning: Compiling in debug mode. Don't use the resulting binary in production) endif diff --git a/cache/clock_cache.cc b/cache/clock_cache.cc index db9d1438e..7e42714ef 100644 --- a/cache/clock_cache.cc +++ b/cache/clock_cache.cc @@ -27,6 +27,11 @@ std::shared_ptr NewClockCache(size_t capacity, int num_shard_bits, #include #include +// "tbb/concurrent_hash_map.h" requires RTTI if exception is enabled. +// Disable it so users can chooose to disable RTTI. +#ifndef ROCKSDB_USE_RTTI +#define TBB_USE_EXCEPTIONS 0 +#endif #include "tbb/concurrent_hash_map.h" #include "cache/sharded_cache.h" diff --git a/db/convenience.cc b/db/convenience.cc index 6568b1fff..e3e7165b4 100644 --- a/db/convenience.cc +++ b/db/convenience.cc @@ -9,16 +9,18 @@ #include "rocksdb/convenience.h" #include "db/db_impl.h" +#include "util/cast_util.h" namespace rocksdb { void CancelAllBackgroundWork(DB* db, bool wait) { - (dynamic_cast(db->GetRootDB()))->CancelAllBackgroundWork(wait); + (static_cast_with_check(db->GetRootDB())) + ->CancelAllBackgroundWork(wait); } Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family, const Slice* begin, const Slice* end) { - return (dynamic_cast(db->GetRootDB())) + return (static_cast_with_check(db->GetRootDB())) ->DeleteFilesInRange(column_family, begin, end); } diff --git a/db/db_impl.h b/db/db_impl.h index 543d64ec8..7fec69cd7 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -235,11 +235,11 @@ class DBImpl : public DB { ColumnFamilyHandle* column_family, ColumnFamilyMetaData* metadata) override; - // experimental API Status SuggestCompactRange(ColumnFamilyHandle* column_family, - const Slice* begin, const Slice* end); + const Slice* begin, const Slice* end) override; - Status PromoteL0(ColumnFamilyHandle* column_family, int target_level); + Status PromoteL0(ColumnFamilyHandle* column_family, + int target_level) override; // Similar to Write() but will call the callback once on the single write // thread to determine whether it is safe to perform the write. diff --git a/db/dbformat.h b/db/dbformat.h index ed1861cf2..d9fd5f399 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -157,6 +157,9 @@ class InternalKeyComparator : public Comparator { int Compare(const InternalKey& a, const InternalKey& b) const; int Compare(const ParsedInternalKey& a, const ParsedInternalKey& b) const; + virtual const Comparator* GetRootComparator() const override { + return user_comparator_->GetRootComparator(); + } }; // Modules in this directory should keep internal keys wrapped inside diff --git a/db/experimental.cc b/db/experimental.cc index 45d4d70aa..effe9d7c3 100644 --- a/db/experimental.cc +++ b/db/experimental.cc @@ -14,20 +14,18 @@ namespace experimental { Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family, const Slice* begin, const Slice* end) { - auto dbimpl = dynamic_cast(db); - if (dbimpl == nullptr) { - return Status::InvalidArgument("Didn't recognize DB object"); + if (db == nullptr) { + return Status::InvalidArgument("DB is empty"); } - return dbimpl->SuggestCompactRange(column_family, begin, end); + return db->SuggestCompactRange(column_family, begin, end); } Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) { - auto dbimpl = dynamic_cast(db); - if (dbimpl == nullptr) { + if (db == nullptr) { return Status::InvalidArgument("Didn't recognize DB object"); } - return dbimpl->PromoteL0(column_family, target_level); + return db->PromoteL0(column_family, target_level); } #else // ROCKSDB_LITE diff --git a/db/wal_manager.cc b/db/wal_manager.cc index 7ee2dd017..4a9ecbfdd 100644 --- a/db/wal_manager.cc +++ b/db/wal_manager.cc @@ -26,6 +26,7 @@ #include "rocksdb/env.h" #include "rocksdb/options.h" #include "rocksdb/write_batch.h" +#include "util/cast_util.h" #include "util/coding.h" #include "util/file_reader_writer.h" #include "util/filename.h" @@ -273,8 +274,8 @@ namespace { struct CompareLogByPointer { bool operator()(const std::unique_ptr& a, const std::unique_ptr& b) { - LogFileImpl* a_impl = dynamic_cast(a.get()); - LogFileImpl* b_impl = dynamic_cast(b.get()); + LogFileImpl* a_impl = static_cast_with_check(a.get()); + LogFileImpl* b_impl = static_cast_with_check(b.get()); return *a_impl < *b_impl; } }; diff --git a/env/mock_env.cc b/env/mock_env.cc index 79a4f8c44..669011c4e 100644 --- a/env/mock_env.cc +++ b/env/mock_env.cc @@ -11,6 +11,7 @@ #include #include #include "port/sys_time.h" +#include "util/cast_util.h" #include "util/murmurhash.h" #include "util/random.h" #include "util/rate_limiter.h" @@ -711,7 +712,8 @@ Status MockEnv::LockFile(const std::string& fname, FileLock** flock) { } Status MockEnv::UnlockFile(FileLock* flock) { - std::string fn = dynamic_cast(flock)->FileName(); + std::string fn = + static_cast_with_check(flock)->FileName(); { MutexLock lock(&mutex_); if (file_map_.find(fn) != file_map_.end()) { diff --git a/examples/Makefile b/examples/Makefile index a3a786e83..57cd1a75a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -8,6 +8,10 @@ ifndef DISABLE_JEMALLOC PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE) endif +ifneq ($(USE_RTTI), 1) + CXXFLAGS += -fno-rtti +endif + .PHONY: clean librocksdb all: simple_example column_families_example compact_files_example c_simple_example optimistic_transaction_example transaction_example compaction_filter_example options_file_example diff --git a/examples/compaction_filter_example.cc b/examples/compaction_filter_example.cc index 7a78244a0..226dfe790 100644 --- a/examples/compaction_filter_example.cc +++ b/examples/compaction_filter_example.cc @@ -59,7 +59,11 @@ int main() { MyFilter filter; - system("rm -rf /tmp/rocksmergetest"); + int ret = system("rm -rf /tmp/rocksmergetest"); + if (ret != 0) { + fprintf(stderr, "Error deleting /tmp/rocksmergetest, code: %d\n", ret); + return ret; + } rocksdb::Options options; options.create_if_missing = true; options.merge_operator.reset(new MyMerge); diff --git a/include/rocksdb/comparator.h b/include/rocksdb/comparator.h index ac6e4a9b0..64db73a72 100644 --- a/include/rocksdb/comparator.h +++ b/include/rocksdb/comparator.h @@ -64,6 +64,10 @@ class Comparator { // Simple comparator implementations may return with *key unchanged, // i.e., an implementation of this method that does nothing is correct. virtual void FindShortSuccessor(std::string* key) const = 0; + + // if it is a wrapped comparator, may return the root one. + // return itself it is not wrapped. + virtual const Comparator* GetRootComparator() const { return this; } }; // Return a builtin comparator that uses lexicographic byte-wise diff --git a/include/rocksdb/db.h b/include/rocksdb/db.h index ee5706b4c..692932c35 100644 --- a/include/rocksdb/db.h +++ b/include/rocksdb/db.h @@ -1097,6 +1097,17 @@ class DB { virtual Status GetPropertiesOfTablesInRange( ColumnFamilyHandle* column_family, const Range* range, std::size_t n, TablePropertiesCollection* props) = 0; + + virtual Status SuggestCompactRange(ColumnFamilyHandle* column_family, + const Slice* begin, const Slice* end) { + return Status::NotSupported("SuggestCompactRange() is not implemented."); + } + + virtual Status PromoteL0(ColumnFamilyHandle* column_family, + int target_level) { + return Status::NotSupported("PromoteL0() is not implemented."); + } + #endif // ROCKSDB_LITE // Needed for StackableDB diff --git a/include/rocksdb/table.h b/include/rocksdb/table.h index 40e4d88b6..04e532e16 100644 --- a/include/rocksdb/table.h +++ b/include/rocksdb/table.h @@ -467,6 +467,12 @@ class TableFactory { // RocksDB prints configurations at DB Open(). virtual std::string GetPrintableTableOptions() const = 0; + virtual Status GetOptionString(std::string* opt_string, + const std::string& delimiter) const { + return Status::NotSupported( + "The table factory doesn't implement GetOptionString()."); + } + // Returns the raw pointer of the table options that is used by this // TableFactory, or nullptr if this function is not supported. // Since the return value is a raw pointer, the TableFactory owns the diff --git a/include/rocksdb/utilities/stackable_db.h b/include/rocksdb/utilities/stackable_db.h index db5068b1d..d2c0dbd7b 100644 --- a/include/rocksdb/utilities/stackable_db.h +++ b/include/rocksdb/utilities/stackable_db.h @@ -350,6 +350,17 @@ class StackableDB : public DB { return db_->GetUpdatesSince(seq_number, iter, read_options); } + virtual Status SuggestCompactRange(ColumnFamilyHandle* column_family, + const Slice* begin, + const Slice* end) override { + return db_->SuggestCompactRange(column_family, begin, end); + } + + virtual Status PromoteL0(ColumnFamilyHandle* column_family, + int target_level) override { + return db_->PromoteL0(column_family, target_level); + } + virtual ColumnFamilyHandle* DefaultColumnFamily() const override { return db_->DefaultColumnFamily(); } diff --git a/monitoring/histogram.cc b/monitoring/histogram.cc index 835ffc88a..56b5a3914 100644 --- a/monitoring/histogram.cc +++ b/monitoring/histogram.cc @@ -19,6 +19,7 @@ #include #include "port/port.h" +#include "util/cast_util.h" namespace rocksdb { @@ -255,7 +256,8 @@ void HistogramImpl::Add(uint64_t value) { void HistogramImpl::Merge(const Histogram& other) { if (strcmp(Name(), other.Name()) == 0) { - Merge(dynamic_cast(other)); + Merge( + *static_cast_with_check(&other)); } } diff --git a/monitoring/histogram_windowing.cc b/monitoring/histogram_windowing.cc index 20ee983f1..28d8265f2 100644 --- a/monitoring/histogram_windowing.cc +++ b/monitoring/histogram_windowing.cc @@ -9,6 +9,7 @@ #include "monitoring/histogram_windowing.h" #include "monitoring/histogram.h" +#include "util/cast_util.h" #include @@ -64,7 +65,9 @@ void HistogramWindowingImpl::Add(uint64_t value){ void HistogramWindowingImpl::Merge(const Histogram& other) { if (strcmp(Name(), other.Name()) == 0) { - Merge(dynamic_cast(other)); + Merge( + *static_cast_with_check( + &other)); } } diff --git a/options/options_helper.cc b/options/options_helper.cc index 9e984f6e3..5cf548fb9 100644 --- a/options/options_helper.cc +++ b/options/options_helper.cc @@ -21,6 +21,7 @@ #include "rocksdb/table.h" #include "table/block_based_table_factory.h" #include "table/plain_table_factory.h" +#include "util/cast_util.h" #include "util/string_util.h" namespace rocksdb { @@ -303,6 +304,7 @@ bool ParseSliceTransform( // SliceTransforms here. return false; } +} // anonymouse namespace bool ParseOptionHelper(char* opt_address, const OptionType& opt_type, const std::string& value) { @@ -383,8 +385,6 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type, return true; } -} // anonymouse namespace - bool SerializeSingleOptionHelper(const char* opt_address, const OptionType opt_type, std::string* value) { @@ -466,12 +466,14 @@ bool SerializeSingleOptionHelper(const char* opt_address, // Since the user-specified comparator will be wrapped by // InternalKeyComparator, we should persist the user-specified one // instead of InternalKeyComparator. - const auto* internal_comparator = - dynamic_cast(*ptr); - if (internal_comparator != nullptr) { - *value = internal_comparator->user_comparator()->Name(); + if (*ptr == nullptr) { + *value = kNullptrString; } else { - *value = *ptr ? (*ptr)->Name() : kNullptrString; + const Comparator* root_comp = (*ptr)->GetRootComparator(); + if (root_comp == nullptr) { + root_comp = (*ptr); + } + *value = root_comp->Name(); } break; } @@ -693,8 +695,9 @@ Status ParseColumnFamilyOption(const std::string& name, if (name == "block_based_table_factory") { // Nested options BlockBasedTableOptions table_opt, base_table_options; - auto block_based_table_factory = dynamic_cast( - new_options->table_factory.get()); + BlockBasedTableFactory* block_based_table_factory = + static_cast_with_check( + new_options->table_factory.get()); if (block_based_table_factory != nullptr) { base_table_options = block_based_table_factory->table_options(); } @@ -708,8 +711,9 @@ Status ParseColumnFamilyOption(const std::string& name, } else if (name == "plain_table_factory") { // Nested options PlainTableOptions table_opt, base_table_options; - auto plain_table_factory = dynamic_cast( - new_options->table_factory.get()); + PlainTableFactory* plain_table_factory = + static_cast_with_check( + new_options->table_factory.get()); if (plain_table_factory != nullptr) { base_table_options = plain_table_factory->table_options(); } @@ -909,59 +913,6 @@ std::vector GetSupportedCompressions() { return supported_compressions; } -bool SerializeSingleBlockBasedTableOption( - std::string* opt_string, const BlockBasedTableOptions& bbt_options, - const std::string& name, const std::string& delimiter) { - auto iter = block_based_table_type_info.find(name); - if (iter == block_based_table_type_info.end()) { - return false; - } - auto& opt_info = iter->second; - const char* opt_address = - reinterpret_cast(&bbt_options) + opt_info.offset; - std::string value; - bool result = SerializeSingleOptionHelper(opt_address, opt_info.type, &value); - if (result) { - *opt_string = name + "=" + value + delimiter; - } - return result; -} - -Status GetStringFromBlockBasedTableOptions( - std::string* opt_string, const BlockBasedTableOptions& bbt_options, - const std::string& delimiter) { - assert(opt_string); - opt_string->clear(); - for (auto iter = block_based_table_type_info.begin(); - iter != block_based_table_type_info.end(); ++iter) { - if (iter->second.verification == OptionVerificationType::kDeprecated) { - // If the option is no longer used in rocksdb and marked as deprecated, - // we skip it in the serialization. - continue; - } - std::string single_output; - bool result = SerializeSingleBlockBasedTableOption( - &single_output, bbt_options, iter->first, delimiter); - assert(result); - if (result) { - opt_string->append(single_output); - } - } - return Status::OK(); -} - -Status GetStringFromTableFactory(std::string* opts_str, const TableFactory* tf, - const std::string& delimiter) { - const auto* bbtf = dynamic_cast(tf); - opts_str->clear(); - if (bbtf != nullptr) { - return GetStringFromBlockBasedTableOptions(opts_str, bbtf->table_options(), - delimiter); - } - - return Status::OK(); -} - Status ParseDBOption(const std::string& name, const std::string& org_value, DBOptions* new_options, @@ -1003,242 +954,6 @@ Status ParseDBOption(const std::string& name, return Status::OK(); } -std::string ParseBlockBasedTableOption(const std::string& name, - const std::string& org_value, - BlockBasedTableOptions* new_options, - bool input_strings_escaped = false, - bool ignore_unknown_options = false) { - const std::string& value = - input_strings_escaped ? UnescapeOptionString(org_value) : org_value; - if (!input_strings_escaped) { - // if the input string is not escaped, it means this function is - // invoked from SetOptions, which takes the old format. - if (name == "block_cache") { - new_options->block_cache = NewLRUCache(ParseSizeT(value)); - return ""; - } else if (name == "block_cache_compressed") { - new_options->block_cache_compressed = NewLRUCache(ParseSizeT(value)); - return ""; - } else if (name == "filter_policy") { - // Expect the following format - // bloomfilter:int:bool - const std::string kName = "bloomfilter:"; - if (value.compare(0, kName.size(), kName) != 0) { - return "Invalid filter policy name"; - } - size_t pos = value.find(':', kName.size()); - if (pos == std::string::npos) { - return "Invalid filter policy config, missing bits_per_key"; - } - int bits_per_key = - ParseInt(trim(value.substr(kName.size(), pos - kName.size()))); - bool use_block_based_builder = - ParseBoolean("use_block_based_builder", trim(value.substr(pos + 1))); - new_options->filter_policy.reset( - NewBloomFilterPolicy(bits_per_key, use_block_based_builder)); - return ""; - } - } - const auto iter = block_based_table_type_info.find(name); - if (iter == block_based_table_type_info.end()) { - if (ignore_unknown_options) { - return ""; - } else { - return "Unrecognized option"; - } - } - const auto& opt_info = iter->second; - if (opt_info.verification != OptionVerificationType::kDeprecated && - !ParseOptionHelper(reinterpret_cast(new_options) + opt_info.offset, - opt_info.type, value)) { - return "Invalid value"; - } - return ""; -} - -std::string ParsePlainTableOptions(const std::string& name, - const std::string& org_value, - PlainTableOptions* new_options, - bool input_strings_escaped = false, - bool ignore_unknown_options = false) { - const std::string& value = - input_strings_escaped ? UnescapeOptionString(org_value) : org_value; - const auto iter = plain_table_type_info.find(name); - if (iter == plain_table_type_info.end()) { - if (ignore_unknown_options) { - return ""; - } else { - return "Unrecognized option"; - } - } - const auto& opt_info = iter->second; - if (opt_info.verification != OptionVerificationType::kDeprecated && - !ParseOptionHelper(reinterpret_cast(new_options) + opt_info.offset, - opt_info.type, value)) { - return "Invalid value"; - } - return ""; -} - -Status GetBlockBasedTableOptionsFromMap( - const BlockBasedTableOptions& table_options, - const std::unordered_map& opts_map, - BlockBasedTableOptions* new_table_options, bool input_strings_escaped, - bool ignore_unknown_options) { - assert(new_table_options); - *new_table_options = table_options; - for (const auto& o : opts_map) { - auto error_message = ParseBlockBasedTableOption( - o.first, o.second, new_table_options, input_strings_escaped, - ignore_unknown_options); - if (error_message != "") { - const auto iter = block_based_table_type_info.find(o.first); - if (iter == block_based_table_type_info.end() || - !input_strings_escaped || // !input_strings_escaped indicates - // the old API, where everything is - // parsable. - (iter->second.verification != OptionVerificationType::kByName && - iter->second.verification != - OptionVerificationType::kByNameAllowNull && - iter->second.verification != - OptionVerificationType::kDeprecated)) { - // Restore "new_options" to the default "base_options". - *new_table_options = table_options; - return Status::InvalidArgument("Can't parse BlockBasedTableOptions:", - o.first + " " + error_message); - } - } - } - return Status::OK(); -} - -Status GetBlockBasedTableOptionsFromString( - const BlockBasedTableOptions& table_options, - const std::string& opts_str, - BlockBasedTableOptions* new_table_options) { - std::unordered_map opts_map; - Status s = StringToMap(opts_str, &opts_map); - if (!s.ok()) { - return s; - } - return GetBlockBasedTableOptionsFromMap(table_options, opts_map, - new_table_options); -} - -Status GetPlainTableOptionsFromMap( - const PlainTableOptions& table_options, - const std::unordered_map& opts_map, - PlainTableOptions* new_table_options, bool input_strings_escaped, - bool ignore_unknown_options) { - assert(new_table_options); - *new_table_options = table_options; - for (const auto& o : opts_map) { - auto error_message = ParsePlainTableOptions( - o.first, o.second, new_table_options, input_strings_escaped); - if (error_message != "") { - const auto iter = plain_table_type_info.find(o.first); - if (iter == plain_table_type_info.end() || - !input_strings_escaped || // !input_strings_escaped indicates - // the old API, where everything is - // parsable. - (iter->second.verification != OptionVerificationType::kByName && - iter->second.verification != - OptionVerificationType::kByNameAllowNull && - iter->second.verification != - OptionVerificationType::kDeprecated)) { - // Restore "new_options" to the default "base_options". - *new_table_options = table_options; - return Status::InvalidArgument("Can't parse PlainTableOptions:", - o.first + " " + error_message); - } - } - } - return Status::OK(); -} - -Status GetPlainTableOptionsFromString( - const PlainTableOptions& table_options, - const std::string& opts_str, - PlainTableOptions* new_table_options) { - std::unordered_map opts_map; - Status s = StringToMap(opts_str, &opts_map); - if (!s.ok()) { - return s; - } - return GetPlainTableOptionsFromMap(table_options, opts_map, - new_table_options); -} - -Status GetMemTableRepFactoryFromString(const std::string& opts_str, - std::unique_ptr* new_mem_factory) { - std::vector opts_list = StringSplit(opts_str, ':'); - size_t len = opts_list.size(); - - if (opts_list.size() <= 0 || opts_list.size() > 2) { - return Status::InvalidArgument("Can't parse memtable_factory option ", - opts_str); - } - - MemTableRepFactory* mem_factory = nullptr; - - if (opts_list[0] == "skip_list") { - // Expecting format - // skip_list: - if (2 == len) { - size_t lookahead = ParseSizeT(opts_list[1]); - mem_factory = new SkipListFactory(lookahead); - } else if (1 == len) { - mem_factory = new SkipListFactory(); - } - } else if (opts_list[0] == "prefix_hash") { - // Expecting format - // prfix_hash: - if (2 == len) { - size_t hash_bucket_count = ParseSizeT(opts_list[1]); - mem_factory = NewHashSkipListRepFactory(hash_bucket_count); - } else if (1 == len) { - mem_factory = NewHashSkipListRepFactory(); - } - } else if (opts_list[0] == "hash_linkedlist") { - // Expecting format - // hash_linkedlist: - if (2 == len) { - size_t hash_bucket_count = ParseSizeT(opts_list[1]); - mem_factory = NewHashLinkListRepFactory(hash_bucket_count); - } else if (1 == len) { - mem_factory = NewHashLinkListRepFactory(); - } - } else if (opts_list[0] == "vector") { - // Expecting format - // vector: - if (2 == len) { - size_t count = ParseSizeT(opts_list[1]); - mem_factory = new VectorRepFactory(count); - } else if (1 == len) { - mem_factory = new VectorRepFactory(); - } - } else if (opts_list[0] == "cuckoo") { - // Expecting format - // cuckoo: - if (2 == len) { - size_t write_buffer_size = ParseSizeT(opts_list[1]); - mem_factory= NewHashCuckooRepFactory(write_buffer_size); - } else if (1 == len) { - return Status::InvalidArgument("Can't parse memtable_factory option ", - opts_str); - } - } else { - return Status::InvalidArgument("Unrecognized memtable_factory option ", - opts_str); - } - - if (mem_factory != nullptr){ - new_mem_factory->reset(mem_factory); - } - - return Status::OK(); -} - Status GetColumnFamilyOptionsFromMap( const ColumnFamilyOptions& base_options, const std::unordered_map& opts_map, diff --git a/options/options_helper.h b/options/options_helper.h index b15faa74f..958612039 100644 --- a/options/options_helper.h +++ b/options/options_helper.h @@ -60,9 +60,6 @@ Status GetTableFactoryFromMap( std::shared_ptr* table_factory, bool ignore_unknown_options = false); -Status GetStringFromTableFactory(std::string* opts_str, const TableFactory* tf, - const std::string& delimiter = "; "); - enum class OptionType { kBoolean, kInt, @@ -580,109 +577,6 @@ static std::unordered_map cf_options_type_info = { {offset_of(&ColumnFamilyOptions::compaction_pri), OptionType::kCompactionPri, OptionVerificationType::kNormal, false, 0}}}; -static std::unordered_map - block_based_table_type_info = { - /* currently not supported - std::shared_ptr block_cache = nullptr; - std::shared_ptr block_cache_compressed = nullptr; - */ - {"flush_block_policy_factory", - {offsetof(struct BlockBasedTableOptions, flush_block_policy_factory), - OptionType::kFlushBlockPolicyFactory, OptionVerificationType::kByName, - false, 0}}, - {"cache_index_and_filter_blocks", - {offsetof(struct BlockBasedTableOptions, - cache_index_and_filter_blocks), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"cache_index_and_filter_blocks_with_high_priority", - {offsetof(struct BlockBasedTableOptions, - cache_index_and_filter_blocks_with_high_priority), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"pin_l0_filter_and_index_blocks_in_cache", - {offsetof(struct BlockBasedTableOptions, - pin_l0_filter_and_index_blocks_in_cache), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"index_type", - {offsetof(struct BlockBasedTableOptions, index_type), - OptionType::kBlockBasedTableIndexType, - OptionVerificationType::kNormal, false, 0}}, - {"hash_index_allow_collision", - {offsetof(struct BlockBasedTableOptions, hash_index_allow_collision), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"checksum", - {offsetof(struct BlockBasedTableOptions, checksum), - OptionType::kChecksumType, OptionVerificationType::kNormal, false, - 0}}, - {"no_block_cache", - {offsetof(struct BlockBasedTableOptions, no_block_cache), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"block_size", - {offsetof(struct BlockBasedTableOptions, block_size), - OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}, - {"block_size_deviation", - {offsetof(struct BlockBasedTableOptions, block_size_deviation), - OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, - {"block_restart_interval", - {offsetof(struct BlockBasedTableOptions, block_restart_interval), - OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, - {"index_block_restart_interval", - {offsetof(struct BlockBasedTableOptions, index_block_restart_interval), - OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, - {"index_per_partition", - {0, OptionType::kUInt64T, OptionVerificationType::kDeprecated, false, - 0}}, - {"metadata_block_size", - {offsetof(struct BlockBasedTableOptions, metadata_block_size), - OptionType::kUInt64T, OptionVerificationType::kNormal, false, 0}}, - {"partition_filters", - {offsetof(struct BlockBasedTableOptions, partition_filters), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"filter_policy", - {offsetof(struct BlockBasedTableOptions, filter_policy), - OptionType::kFilterPolicy, OptionVerificationType::kByName, false, - 0}}, - {"whole_key_filtering", - {offsetof(struct BlockBasedTableOptions, whole_key_filtering), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"skip_table_builder_flush", - {0, OptionType::kBoolean, OptionVerificationType::kDeprecated, false, - 0}}, - {"format_version", - {offsetof(struct BlockBasedTableOptions, format_version), - OptionType::kUInt32T, OptionVerificationType::kNormal, false, 0}}, - {"verify_compression", - {offsetof(struct BlockBasedTableOptions, verify_compression), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, - {"read_amp_bytes_per_bit", - {offsetof(struct BlockBasedTableOptions, read_amp_bytes_per_bit), - OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}}; - -static std::unordered_map plain_table_type_info = { - {"user_key_len", - {offsetof(struct PlainTableOptions, user_key_len), OptionType::kUInt32T, - OptionVerificationType::kNormal, false, 0}}, - {"bloom_bits_per_key", - {offsetof(struct PlainTableOptions, bloom_bits_per_key), OptionType::kInt, - OptionVerificationType::kNormal, false, 0}}, - {"hash_table_ratio", - {offsetof(struct PlainTableOptions, hash_table_ratio), OptionType::kDouble, - OptionVerificationType::kNormal, false, 0}}, - {"index_sparseness", - {offsetof(struct PlainTableOptions, index_sparseness), OptionType::kSizeT, - OptionVerificationType::kNormal, false, 0}}, - {"huge_page_tlb_size", - {offsetof(struct PlainTableOptions, huge_page_tlb_size), - OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}, - {"encoding_type", - {offsetof(struct PlainTableOptions, encoding_type), - OptionType::kEncodingType, OptionVerificationType::kByName, false, 0}}, - {"full_scan_mode", - {offsetof(struct PlainTableOptions, full_scan_mode), OptionType::kBoolean, - OptionVerificationType::kNormal, false, 0}}, - {"store_index_in_file", - {offsetof(struct PlainTableOptions, store_index_in_file), - OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}}; - static std::unordered_map compression_type_string_map = { {"kNoCompression", kNoCompression}, @@ -745,6 +639,12 @@ static std::unordered_map info_log_level_string_map = {"FATAL_LEVEL", InfoLogLevel::FATAL_LEVEL}, {"HEADER_LEVEL", InfoLogLevel::HEADER_LEVEL}}; +extern Status StringToMap( + const std::string& opts_str, + std::unordered_map* opts_map); + +extern bool ParseOptionHelper(char* opt_address, const OptionType& opt_type, + const std::string& value); #endif // !ROCKSDB_LITE } // namespace rocksdb diff --git a/options/options_parser.cc b/options/options_parser.cc index d5a3fec6e..2cb60a068 100644 --- a/options/options_parser.cc +++ b/options/options_parser.cc @@ -16,6 +16,7 @@ #include "options/options_helper.h" #include "rocksdb/convenience.h" #include "rocksdb/db.h" +#include "util/cast_util.h" #include "util/string_util.h" #include "util/sync_point.h" @@ -84,7 +85,8 @@ Status PersistRocksDBOptions(const DBOptions& db_opt, writable->Append("[" + opt_section_titles[kOptionSectionTableOptions] + tf->Name() + " \"" + EscapeOptionString(cf_names[i]) + "\"]\n "); - s = GetStringFromTableFactory(&options_file_content, tf, "\n "); + options_file_content.clear(); + s = tf->GetOptionString(&options_file_content, "\n "); if (!s.ok()) { return s; } @@ -507,6 +509,7 @@ namespace { bool AreEqualDoubles(const double a, const double b) { return (fabs(a - b) < 0.00001); } +} // namespace bool AreEqualOptions( const char* opt1, const char* opt2, const OptionTypeInfo& type_info, @@ -613,8 +616,6 @@ bool AreEqualOptions( } } -} // namespace - Status RocksDBOptionsParser::VerifyRocksDBOptionsFromFile( const DBOptions& db_opt, const std::vector& cf_names, const std::vector& cf_opts, @@ -762,59 +763,23 @@ Status RocksDBOptionsParser::VerifyCFOptions( return Status::OK(); } -Status RocksDBOptionsParser::VerifyBlockBasedTableFactory( - const BlockBasedTableFactory* base_tf, - const BlockBasedTableFactory* file_tf, - OptionsSanityCheckLevel sanity_check_level) { - if ((base_tf != nullptr) != (file_tf != nullptr) && - sanity_check_level > kSanityLevelNone) { - return Status::Corruption( - "[RocksDBOptionsParser]: Inconsistent TableFactory class type"); - } - if (base_tf == nullptr) { - return Status::OK(); - } - assert(file_tf != nullptr); - - const auto& base_opt = base_tf->table_options(); - const auto& file_opt = file_tf->table_options(); - - for (auto& pair : block_based_table_type_info) { - if (pair.second.verification == OptionVerificationType::kDeprecated) { - // We skip checking deprecated variables as they might - // contain random values since they might not be initialized - continue; - } - if (BBTOptionSanityCheckLevel(pair.first) <= sanity_check_level) { - if (!AreEqualOptions(reinterpret_cast(&base_opt), - reinterpret_cast(&file_opt), - pair.second, pair.first, nullptr)) { - return Status::Corruption( - "[RocksDBOptionsParser]: " - "failed the verification on BlockBasedTableOptions::", - pair.first); - } - } - } - return Status::OK(); -} - Status RocksDBOptionsParser::VerifyTableFactory( const TableFactory* base_tf, const TableFactory* file_tf, OptionsSanityCheckLevel sanity_check_level) { if (base_tf && file_tf) { if (sanity_check_level > kSanityLevelNone && - base_tf->Name() != file_tf->Name()) { + std::string(base_tf->Name()) != std::string(file_tf->Name())) { return Status::Corruption( "[RocksDBOptionsParser]: " "failed the verification on TableFactory->Name()"); } - auto s = VerifyBlockBasedTableFactory( - dynamic_cast(base_tf), - dynamic_cast(file_tf), - sanity_check_level); - if (!s.ok()) { - return s; + if (base_tf->Name() == BlockBasedTableFactory::kName) { + return VerifyBlockBasedTableFactory( + static_cast_with_check(base_tf), + static_cast_with_check(file_tf), + sanity_check_level); } // TODO(yhchiang): add checks for other table factory types } else { diff --git a/options/options_parser.h b/options/options_parser.h index cae3dbba9..5545c0b0f 100644 --- a/options/options_parser.h +++ b/options/options_parser.h @@ -38,6 +38,11 @@ Status PersistRocksDBOptions(const DBOptions& db_opt, const std::vector& cf_opts, const std::string& file_name, Env* env); +extern bool AreEqualOptions( + const char* opt1, const char* opt2, const OptionTypeInfo& type_info, + const std::string& opt_name, + const std::unordered_map* opt_map); + class RocksDBOptionsParser { public: explicit RocksDBOptionsParser(); @@ -86,11 +91,6 @@ class RocksDBOptionsParser { const TableFactory* base_tf, const TableFactory* file_tf, OptionsSanityCheckLevel sanity_check_level = kSanityLevelExactMatch); - static Status VerifyBlockBasedTableFactory( - const BlockBasedTableFactory* base_tf, - const BlockBasedTableFactory* file_tf, - OptionsSanityCheckLevel sanity_check_level); - static Status ExtraParserCheck(const RocksDBOptionsParser& input_parser); protected: diff --git a/options/options_test.cc b/options/options_test.cc index d5eb42b09..fc4939beb 100644 --- a/options/options_test.cc +++ b/options/options_test.cc @@ -889,11 +889,11 @@ TEST_F(OptionsTest, ConvertOptionsTest) { ASSERT_EQ(converted_opt.max_open_files, leveldb_opt.max_open_files); ASSERT_EQ(converted_opt.compression, leveldb_opt.compression); - std::shared_ptr table_factory = - std::dynamic_pointer_cast( - converted_opt.table_factory); + std::shared_ptr tb_guard = converted_opt.table_factory; + BlockBasedTableFactory* table_factory = + dynamic_cast(converted_opt.table_factory.get()); - ASSERT_TRUE(table_factory.get() != nullptr); + ASSERT_TRUE(table_factory != nullptr); const BlockBasedTableOptions table_opt = table_factory->table_options(); @@ -1278,6 +1278,11 @@ TEST_F(OptionsParserTest, DumpAndParse) { Random rnd(302); test::RandomInitDBOptions(&base_db_opt, &rnd); base_db_opt.db_log_dir += "/#odd #but #could #happen #path #/\\\\#OMG"; + + BlockBasedTableOptions special_bbto; + special_bbto.cache_index_and_filter_blocks = true; + special_bbto.block_size = 999999; + for (int c = 0; c < num_cf; ++c) { ColumnFamilyOptions cf_opt; Random cf_rnd(0xFB + c); @@ -1287,6 +1292,8 @@ TEST_F(OptionsParserTest, DumpAndParse) { } if (c < 3) { cf_opt.table_factory.reset(test::RandomTableFactory(&rnd, c)); + } else if (c == 4) { + cf_opt.table_factory.reset(NewBlockBasedTableFactory(special_bbto)); } base_cf_opts.emplace_back(cf_opt); } @@ -1298,6 +1305,15 @@ TEST_F(OptionsParserTest, DumpAndParse) { RocksDBOptionsParser parser; ASSERT_OK(parser.Parse(kOptionsFileName, env_.get())); + // Make sure block-based table factory options was deserialized correctly + std::shared_ptr ttf = (*parser.cf_opts())[4].table_factory; + ASSERT_EQ(BlockBasedTableFactory::kName, std::string(ttf->Name())); + const BlockBasedTableOptions& parsed_bbto = + static_cast(ttf.get())->table_options(); + ASSERT_EQ(special_bbto.block_size, parsed_bbto.block_size); + ASSERT_EQ(special_bbto.cache_index_and_filter_blocks, + parsed_bbto.cache_index_and_filter_blocks); + ASSERT_OK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile( base_db_opt, cf_names, base_cf_opts, kOptionsFileName, env_.get())); diff --git a/table/block_based_table_factory.cc b/table/block_based_table_factory.cc index 4705046bf..b4f8ba8a1 100644 --- a/table/block_based_table_factory.cc +++ b/table/block_based_table_factory.cc @@ -13,12 +13,15 @@ #include #include +#include "options/options_helper.h" #include "port/port.h" -#include "rocksdb/flush_block_policy.h" #include "rocksdb/cache.h" +#include "rocksdb/convenience.h" +#include "rocksdb/flush_block_policy.h" #include "table/block_based_table_builder.h" #include "table/block_based_table_reader.h" #include "table/format.h" +#include "util/string_util.h" namespace rocksdb { @@ -201,15 +204,203 @@ std::string BlockBasedTableFactory::GetPrintableTableOptions() const { return ret; } +#ifndef ROCKSDB_LITE +namespace { +bool SerializeSingleBlockBasedTableOption( + std::string* opt_string, const BlockBasedTableOptions& bbt_options, + const std::string& name, const std::string& delimiter) { + auto iter = block_based_table_type_info.find(name); + if (iter == block_based_table_type_info.end()) { + return false; + } + auto& opt_info = iter->second; + const char* opt_address = + reinterpret_cast(&bbt_options) + opt_info.offset; + std::string value; + bool result = SerializeSingleOptionHelper(opt_address, opt_info.type, &value); + if (result) { + *opt_string = name + "=" + value + delimiter; + } + return result; +} +} // namespace + +Status BlockBasedTableFactory::GetOptionString( + std::string* opt_string, const std::string& delimiter) const { + assert(opt_string); + opt_string->clear(); + for (auto iter = block_based_table_type_info.begin(); + iter != block_based_table_type_info.end(); ++iter) { + if (iter->second.verification == OptionVerificationType::kDeprecated) { + // If the option is no longer used in rocksdb and marked as deprecated, + // we skip it in the serialization. + continue; + } + std::string single_output; + bool result = SerializeSingleBlockBasedTableOption( + &single_output, table_options_, iter->first, delimiter); + assert(result); + if (result) { + opt_string->append(single_output); + } + } + return Status::OK(); +} +#else +Status BlockBasedTableFactory::GetOptionString( + std::string* opt_string, const std::string& delimiter) const { + return Status::OK(); +} +#endif // !ROCKSDB_LITE + const BlockBasedTableOptions& BlockBasedTableFactory::table_options() const { return table_options_; } +#ifndef ROCKSDB_LITE +namespace { +std::string ParseBlockBasedTableOption(const std::string& name, + const std::string& org_value, + BlockBasedTableOptions* new_options, + bool input_strings_escaped = false, + bool ignore_unknown_options = false) { + const std::string& value = + input_strings_escaped ? UnescapeOptionString(org_value) : org_value; + if (!input_strings_escaped) { + // if the input string is not escaped, it means this function is + // invoked from SetOptions, which takes the old format. + if (name == "block_cache") { + new_options->block_cache = NewLRUCache(ParseSizeT(value)); + return ""; + } else if (name == "block_cache_compressed") { + new_options->block_cache_compressed = NewLRUCache(ParseSizeT(value)); + return ""; + } else if (name == "filter_policy") { + // Expect the following format + // bloomfilter:int:bool + const std::string kName = "bloomfilter:"; + if (value.compare(0, kName.size(), kName) != 0) { + return "Invalid filter policy name"; + } + size_t pos = value.find(':', kName.size()); + if (pos == std::string::npos) { + return "Invalid filter policy config, missing bits_per_key"; + } + int bits_per_key = + ParseInt(trim(value.substr(kName.size(), pos - kName.size()))); + bool use_block_based_builder = + ParseBoolean("use_block_based_builder", trim(value.substr(pos + 1))); + new_options->filter_policy.reset( + NewBloomFilterPolicy(bits_per_key, use_block_based_builder)); + return ""; + } + } + const auto iter = block_based_table_type_info.find(name); + if (iter == block_based_table_type_info.end()) { + if (ignore_unknown_options) { + return ""; + } else { + return "Unrecognized option"; + } + } + const auto& opt_info = iter->second; + if (opt_info.verification != OptionVerificationType::kDeprecated && + !ParseOptionHelper(reinterpret_cast(new_options) + opt_info.offset, + opt_info.type, value)) { + return "Invalid value"; + } + return ""; +} +} // namespace + +Status GetBlockBasedTableOptionsFromString( + const BlockBasedTableOptions& table_options, const std::string& opts_str, + BlockBasedTableOptions* new_table_options) { + std::unordered_map opts_map; + Status s = StringToMap(opts_str, &opts_map); + if (!s.ok()) { + return s; + } + + return GetBlockBasedTableOptionsFromMap(table_options, opts_map, + new_table_options); +} + +Status GetBlockBasedTableOptionsFromMap( + const BlockBasedTableOptions& table_options, + const std::unordered_map& opts_map, + BlockBasedTableOptions* new_table_options, bool input_strings_escaped, + bool ignore_unknown_options) { + assert(new_table_options); + *new_table_options = table_options; + for (const auto& o : opts_map) { + auto error_message = ParseBlockBasedTableOption( + o.first, o.second, new_table_options, input_strings_escaped, + ignore_unknown_options); + if (error_message != "") { + const auto iter = block_based_table_type_info.find(o.first); + if (iter == block_based_table_type_info.end() || + !input_strings_escaped || // !input_strings_escaped indicates + // the old API, where everything is + // parsable. + (iter->second.verification != OptionVerificationType::kByName && + iter->second.verification != + OptionVerificationType::kByNameAllowNull && + iter->second.verification != OptionVerificationType::kDeprecated)) { + // Restore "new_options" to the default "base_options". + *new_table_options = table_options; + return Status::InvalidArgument("Can't parse BlockBasedTableOptions:", + o.first + " " + error_message); + } + } + } + return Status::OK(); +} + +Status VerifyBlockBasedTableFactory( + const BlockBasedTableFactory* base_tf, + const BlockBasedTableFactory* file_tf, + OptionsSanityCheckLevel sanity_check_level) { + if ((base_tf != nullptr) != (file_tf != nullptr) && + sanity_check_level > kSanityLevelNone) { + return Status::Corruption( + "[RocksDBOptionsParser]: Inconsistent TableFactory class type"); + } + if (base_tf == nullptr) { + return Status::OK(); + } + assert(file_tf != nullptr); + + const auto& base_opt = base_tf->table_options(); + const auto& file_opt = file_tf->table_options(); + + for (auto& pair : block_based_table_type_info) { + if (pair.second.verification == OptionVerificationType::kDeprecated) { + // We skip checking deprecated variables as they might + // contain random values since they might not be initialized + continue; + } + if (BBTOptionSanityCheckLevel(pair.first) <= sanity_check_level) { + if (!AreEqualOptions(reinterpret_cast(&base_opt), + reinterpret_cast(&file_opt), + pair.second, pair.first, nullptr)) { + return Status::Corruption( + "[RocksDBOptionsParser]: " + "failed the verification on BlockBasedTableOptions::", + pair.first); + } + } + } + return Status::OK(); +} +#endif // !ROCKSDB_LITE + TableFactory* NewBlockBasedTableFactory( const BlockBasedTableOptions& _table_options) { return new BlockBasedTableFactory(_table_options); } +const std::string BlockBasedTableFactory::kName = "BlockBasedTable"; const std::string BlockBasedTablePropertyNames::kIndexType = "rocksdb.block.based.table.index.type"; const std::string BlockBasedTablePropertyNames::kWholeKeyFiltering = diff --git a/table/block_based_table_factory.h b/table/block_based_table_factory.h index bdff00d1e..39e3eac0b 100644 --- a/table/block_based_table_factory.h +++ b/table/block_based_table_factory.h @@ -13,9 +13,11 @@ #include #include +#include "db/dbformat.h" +#include "options/options_helper.h" +#include "options/options_parser.h" #include "rocksdb/flush_block_policy.h" #include "rocksdb/table.h" -#include "db/dbformat.h" namespace rocksdb { @@ -31,7 +33,7 @@ class BlockBasedTableFactory : public TableFactory { ~BlockBasedTableFactory() {} - const char* Name() const override { return "BlockBasedTable"; } + const char* Name() const override { return kName.c_str(); } Status NewTableReader( const TableReaderOptions& table_reader_options, @@ -49,12 +51,17 @@ class BlockBasedTableFactory : public TableFactory { std::string GetPrintableTableOptions() const override; + Status GetOptionString(std::string* opt_string, + const std::string& delimiter) const override; + const BlockBasedTableOptions& table_options() const; void* GetOptions() override { return &table_options_; } bool IsDeleteRangeSupported() const override { return true; } + static const std::string kName; + private: BlockBasedTableOptions table_options_; }; @@ -64,4 +71,87 @@ extern const std::string kHashIndexPrefixesMetadataBlock; extern const std::string kPropTrue; extern const std::string kPropFalse; +#ifndef ROCKSDB_LITE +extern Status VerifyBlockBasedTableFactory( + const BlockBasedTableFactory* base_tf, + const BlockBasedTableFactory* file_tf, + OptionsSanityCheckLevel sanity_check_level); + +static std::unordered_map + block_based_table_type_info = { + /* currently not supported + std::shared_ptr block_cache = nullptr; + std::shared_ptr block_cache_compressed = nullptr; + */ + {"flush_block_policy_factory", + {offsetof(struct BlockBasedTableOptions, flush_block_policy_factory), + OptionType::kFlushBlockPolicyFactory, OptionVerificationType::kByName, + false, 0}}, + {"cache_index_and_filter_blocks", + {offsetof(struct BlockBasedTableOptions, + cache_index_and_filter_blocks), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"cache_index_and_filter_blocks_with_high_priority", + {offsetof(struct BlockBasedTableOptions, + cache_index_and_filter_blocks_with_high_priority), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"pin_l0_filter_and_index_blocks_in_cache", + {offsetof(struct BlockBasedTableOptions, + pin_l0_filter_and_index_blocks_in_cache), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"index_type", + {offsetof(struct BlockBasedTableOptions, index_type), + OptionType::kBlockBasedTableIndexType, + OptionVerificationType::kNormal, false, 0}}, + {"hash_index_allow_collision", + {offsetof(struct BlockBasedTableOptions, hash_index_allow_collision), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"checksum", + {offsetof(struct BlockBasedTableOptions, checksum), + OptionType::kChecksumType, OptionVerificationType::kNormal, false, + 0}}, + {"no_block_cache", + {offsetof(struct BlockBasedTableOptions, no_block_cache), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"block_size", + {offsetof(struct BlockBasedTableOptions, block_size), + OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}, + {"block_size_deviation", + {offsetof(struct BlockBasedTableOptions, block_size_deviation), + OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, + {"block_restart_interval", + {offsetof(struct BlockBasedTableOptions, block_restart_interval), + OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, + {"index_block_restart_interval", + {offsetof(struct BlockBasedTableOptions, index_block_restart_interval), + OptionType::kInt, OptionVerificationType::kNormal, false, 0}}, + {"index_per_partition", + {0, OptionType::kUInt64T, OptionVerificationType::kDeprecated, false, + 0}}, + {"metadata_block_size", + {offsetof(struct BlockBasedTableOptions, metadata_block_size), + OptionType::kUInt64T, OptionVerificationType::kNormal, false, 0}}, + {"partition_filters", + {offsetof(struct BlockBasedTableOptions, partition_filters), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"filter_policy", + {offsetof(struct BlockBasedTableOptions, filter_policy), + OptionType::kFilterPolicy, OptionVerificationType::kByName, false, + 0}}, + {"whole_key_filtering", + {offsetof(struct BlockBasedTableOptions, whole_key_filtering), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"skip_table_builder_flush", + {0, OptionType::kBoolean, OptionVerificationType::kDeprecated, false, + 0}}, + {"format_version", + {offsetof(struct BlockBasedTableOptions, format_version), + OptionType::kUInt32T, OptionVerificationType::kNormal, false, 0}}, + {"verify_compression", + {offsetof(struct BlockBasedTableOptions, verify_compression), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}, + {"read_amp_bytes_per_bit", + {offsetof(struct BlockBasedTableOptions, read_amp_bytes_per_bit), + OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}}; +#endif // !ROCKSDB_LITE } // namespace rocksdb diff --git a/table/cuckoo_table_factory.h b/table/cuckoo_table_factory.h index 774dc3c3e..db860c3d0 100644 --- a/table/cuckoo_table_factory.h +++ b/table/cuckoo_table_factory.h @@ -76,6 +76,11 @@ class CuckooTableFactory : public TableFactory { void* GetOptions() override { return &table_options_; } + Status GetOptionString(std::string* opt_string, + const std::string& delimiter) const override { + return Status::OK(); + } + private: CuckooTableOptions table_options_; }; diff --git a/table/plain_table_factory.cc b/table/plain_table_factory.cc index eadc2c099..5f7809b96 100644 --- a/table/plain_table_factory.cc +++ b/table/plain_table_factory.cc @@ -5,12 +5,15 @@ #ifndef ROCKSDB_LITE #include "table/plain_table_factory.h" -#include #include +#include #include "db/dbformat.h" +#include "options/options_helper.h" +#include "port/port.h" +#include "rocksdb/convenience.h" #include "table/plain_table_builder.h" #include "table/plain_table_reader.h" -#include "port/port.h" +#include "util/string_util.h" namespace rocksdb { @@ -81,6 +84,143 @@ const PlainTableOptions& PlainTableFactory::table_options() const { return table_options_; } +Status GetPlainTableOptionsFromString(const PlainTableOptions& table_options, + const std::string& opts_str, + PlainTableOptions* new_table_options) { + std::unordered_map opts_map; + Status s = StringToMap(opts_str, &opts_map); + if (!s.ok()) { + return s; + } + return GetPlainTableOptionsFromMap(table_options, opts_map, + new_table_options); +} + +Status GetMemTableRepFactoryFromString( + const std::string& opts_str, + std::unique_ptr* new_mem_factory) { + std::vector opts_list = StringSplit(opts_str, ':'); + size_t len = opts_list.size(); + + if (opts_list.size() <= 0 || opts_list.size() > 2) { + return Status::InvalidArgument("Can't parse memtable_factory option ", + opts_str); + } + + MemTableRepFactory* mem_factory = nullptr; + + if (opts_list[0] == "skip_list") { + // Expecting format + // skip_list: + if (2 == len) { + size_t lookahead = ParseSizeT(opts_list[1]); + mem_factory = new SkipListFactory(lookahead); + } else if (1 == len) { + mem_factory = new SkipListFactory(); + } + } else if (opts_list[0] == "prefix_hash") { + // Expecting format + // prfix_hash: + if (2 == len) { + size_t hash_bucket_count = ParseSizeT(opts_list[1]); + mem_factory = NewHashSkipListRepFactory(hash_bucket_count); + } else if (1 == len) { + mem_factory = NewHashSkipListRepFactory(); + } + } else if (opts_list[0] == "hash_linkedlist") { + // Expecting format + // hash_linkedlist: + if (2 == len) { + size_t hash_bucket_count = ParseSizeT(opts_list[1]); + mem_factory = NewHashLinkListRepFactory(hash_bucket_count); + } else if (1 == len) { + mem_factory = NewHashLinkListRepFactory(); + } + } else if (opts_list[0] == "vector") { + // Expecting format + // vector: + if (2 == len) { + size_t count = ParseSizeT(opts_list[1]); + mem_factory = new VectorRepFactory(count); + } else if (1 == len) { + mem_factory = new VectorRepFactory(); + } + } else if (opts_list[0] == "cuckoo") { + // Expecting format + // cuckoo: + if (2 == len) { + size_t write_buffer_size = ParseSizeT(opts_list[1]); + mem_factory = NewHashCuckooRepFactory(write_buffer_size); + } else if (1 == len) { + return Status::InvalidArgument("Can't parse memtable_factory option ", + opts_str); + } + } else { + return Status::InvalidArgument("Unrecognized memtable_factory option ", + opts_str); + } + + if (mem_factory != nullptr) { + new_mem_factory->reset(mem_factory); + } + + return Status::OK(); +} + +std::string ParsePlainTableOptions(const std::string& name, + const std::string& org_value, + PlainTableOptions* new_options, + bool input_strings_escaped = false, + bool ignore_unknown_options = false) { + const std::string& value = + input_strings_escaped ? UnescapeOptionString(org_value) : org_value; + const auto iter = plain_table_type_info.find(name); + if (iter == plain_table_type_info.end()) { + if (ignore_unknown_options) { + return ""; + } else { + return "Unrecognized option"; + } + } + const auto& opt_info = iter->second; + if (opt_info.verification != OptionVerificationType::kDeprecated && + !ParseOptionHelper(reinterpret_cast(new_options) + opt_info.offset, + opt_info.type, value)) { + return "Invalid value"; + } + return ""; +} + +Status GetPlainTableOptionsFromMap( + const PlainTableOptions& table_options, + const std::unordered_map& opts_map, + PlainTableOptions* new_table_options, bool input_strings_escaped, + bool ignore_unknown_options) { + assert(new_table_options); + *new_table_options = table_options; + for (const auto& o : opts_map) { + auto error_message = ParsePlainTableOptions( + o.first, o.second, new_table_options, input_strings_escaped); + if (error_message != "") { + const auto iter = plain_table_type_info.find(o.first); + if (iter == plain_table_type_info.end() || + !input_strings_escaped || // !input_strings_escaped indicates + // the old API, where everything is + // parsable. + (iter->second.verification != OptionVerificationType::kByName && + iter->second.verification != + OptionVerificationType::kByNameAllowNull && + iter->second.verification != OptionVerificationType::kDeprecated)) { + // Restore "new_options" to the default "base_options". + *new_table_options = table_options; + return Status::InvalidArgument("Can't parse PlainTableOptions:", + o.first + " " + error_message); + } + } + } + return Status::OK(); +} + extern TableFactory* NewPlainTableFactory(const PlainTableOptions& options) { return new PlainTableFactory(options); } diff --git a/table/plain_table_factory.h b/table/plain_table_factory.h index 33cd31347..6c9ca44f3 100644 --- a/table/plain_table_factory.h +++ b/table/plain_table_factory.h @@ -9,6 +9,7 @@ #include #include +#include "options/options_helper.h" #include "rocksdb/options.h" #include "rocksdb/table.h" @@ -170,9 +171,40 @@ class PlainTableFactory : public TableFactory { void* GetOptions() override { return &table_options_; } + Status GetOptionString(std::string* opt_string, + const std::string& delimiter) const override { + return Status::OK(); + } + private: PlainTableOptions table_options_; }; +static std::unordered_map plain_table_type_info = { + {"user_key_len", + {offsetof(struct PlainTableOptions, user_key_len), OptionType::kUInt32T, + OptionVerificationType::kNormal, false, 0}}, + {"bloom_bits_per_key", + {offsetof(struct PlainTableOptions, bloom_bits_per_key), OptionType::kInt, + OptionVerificationType::kNormal, false, 0}}, + {"hash_table_ratio", + {offsetof(struct PlainTableOptions, hash_table_ratio), OptionType::kDouble, + OptionVerificationType::kNormal, false, 0}}, + {"index_sparseness", + {offsetof(struct PlainTableOptions, index_sparseness), OptionType::kSizeT, + OptionVerificationType::kNormal, false, 0}}, + {"huge_page_tlb_size", + {offsetof(struct PlainTableOptions, huge_page_tlb_size), + OptionType::kSizeT, OptionVerificationType::kNormal, false, 0}}, + {"encoding_type", + {offsetof(struct PlainTableOptions, encoding_type), + OptionType::kEncodingType, OptionVerificationType::kByName, false, 0}}, + {"full_scan_mode", + {offsetof(struct PlainTableOptions, full_scan_mode), OptionType::kBoolean, + OptionVerificationType::kNormal, false, 0}}, + {"store_index_in_file", + {offsetof(struct PlainTableOptions, store_index_in_file), + OptionType::kBoolean, OptionVerificationType::kNormal, false, 0}}}; + } // namespace rocksdb #endif // ROCKSDB_LITE diff --git a/tools/db_bench_tool.cc b/tools/db_bench_tool.cc index d10758f04..a1c6af029 100644 --- a/tools/db_bench_tool.cc +++ b/tools/db_bench_tool.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include "rocksdb/utilities/transaction.h" #include "rocksdb/utilities/transaction_db.h" #include "rocksdb/write_batch.h" +#include "util/cast_util.h" #include "util/compression.h" #include "util/crc32c.h" #include "util/mutexlock.h" @@ -2551,7 +2553,9 @@ void VerifyDBFromDB(std::string& truth_db_name) { } if (FLAGS_simcache_size >= 0) { fprintf(stdout, "SIMULATOR CACHE STATISTICS:\n%s\n", - std::dynamic_pointer_cast(cache_)->ToString().c_str()); + static_cast_with_check(cache_.get()) + ->ToString() + .c_str()); } } diff --git a/tools/ldb_cmd.cc b/tools/ldb_cmd.cc index 2cd4d94d1..c8b6221a5 100644 --- a/tools/ldb_cmd.cc +++ b/tools/ldb_cmd.cc @@ -29,6 +29,7 @@ #include "table/scoped_arena_iterator.h" #include "tools/ldb_cmd_impl.h" #include "tools/sst_dump_tool_imp.h" +#include "util/cast_util.h" #include "util/coding.h" #include "util/filename.h" #include "util/stderr_logger.h" @@ -1493,8 +1494,7 @@ void DBDumperCommand::DoDumpCommand() { if (max_keys == 0) break; if (is_db_ttl_) { - TtlIterator* it_ttl = dynamic_cast(iter); - assert(it_ttl); + TtlIterator* it_ttl = static_cast_with_check(iter); rawtime = it_ttl->timestamp(); if (rawtime < ttl_start || rawtime >= ttl_end) { continue; @@ -2291,8 +2291,7 @@ void ScanCommand::DoCommand() { it->Valid() && (!end_key_specified_ || it->key().ToString() < end_key_); it->Next()) { if (is_db_ttl_) { - TtlIterator* it_ttl = dynamic_cast(it); - assert(it_ttl); + TtlIterator* it_ttl = static_cast_with_check(it); int rawtime = it_ttl->timestamp(); if (rawtime < ttl_start || rawtime >= ttl_end) { continue; diff --git a/tools/sst_dump_tool.cc b/tools/sst_dump_tool.cc index 2a1729c76..07f348612 100644 --- a/tools/sst_dump_tool.cc +++ b/tools/sst_dump_tool.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -42,8 +43,6 @@ namespace rocksdb { -using std::dynamic_pointer_cast; - SstFileReader::SstFileReader(const std::string& file_path, bool verify_checksum, bool output_hex) @@ -115,18 +114,13 @@ Status SstFileReader::NewTableReader( unique_ptr* table_reader) { // We need to turn off pre-fetching of index and filter nodes for // BlockBasedTable - shared_ptr block_table_factory = - dynamic_pointer_cast(options_.table_factory); - - if (block_table_factory) { - return block_table_factory->NewTableReader( + if (BlockBasedTableFactory::kName == options_.table_factory->Name()) { + return options_.table_factory->NewTableReader( TableReaderOptions(ioptions_, soptions_, internal_comparator_, /*skip_filters=*/false), std::move(file_), file_size, &table_reader_, /*enable_prefetch=*/false); } - assert(!block_table_factory); - // For all other factory implementation return options_.table_factory->NewTableReader( TableReaderOptions(ioptions_, soptions_, internal_comparator_), diff --git a/util/cast_util.h b/util/cast_util.h new file mode 100644 index 000000000..b42d5d6c4 --- /dev/null +++ b/util/cast_util.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +namespace rocksdb { +// The helper function to assert the move from dynamic_cast<> to +// static_cast<> is correct. This function is to deal with legacy code. +// It is not recommanded to add new code to issue class casting. The preferred +// solution is to implement the functionality without a need of casting. +template +inline DestClass* static_cast_with_check(SrcClass* x) { + DestClass* ret = static_cast(x); +#ifdef ROCKSDB_USE_RTTI + assert(ret == dynamic_cast(x)); +#endif + return ret; +} +} // namespace rocksdb diff --git a/utilities/blob_db/blob_db_impl.cc b/utilities/blob_db/blob_db_impl.cc index 95deda5b0..f55566fd4 100644 --- a/utilities/blob_db/blob_db_impl.cc +++ b/utilities/blob_db/blob_db_impl.cc @@ -23,6 +23,7 @@ #include "table/block_based_table_builder.h" #include "table/block_builder.h" #include "table/meta_blocks.h" +#include "util/cast_util.h" #include "util/crc32c.h" #include "util/file_reader_writer.h" #include "util/filename.h" @@ -199,7 +200,8 @@ BlobDBImpl::BlobDBImpl(const std::string& dbname, open_p1_done_(false), debug_level_(0) { const BlobDBOptionsImpl* options_impl = - dynamic_cast(&blob_db_options); + static_cast_with_check( + &blob_db_options); if (options_impl) { bdb_options_ = *options_impl; } @@ -215,12 +217,7 @@ Status BlobDBImpl::LinkToBaseDB(DB* db) { db_ = db; // the Base DB in-itself can be a stackable DB - StackableDB* sdb = dynamic_cast(db_); - if (sdb) { - db_impl_ = dynamic_cast(sdb->GetBaseDB()); - } else { - db_impl_ = dynamic_cast(db); - } + db_impl_ = static_cast_with_check(db_->GetRootDB()); env_ = db_->GetEnv(); @@ -249,7 +246,7 @@ BlobDBOptions BlobDBImpl::GetBlobDBOptions() const { return bdb_options_; } BlobDBImpl::BlobDBImpl(DB* db, const BlobDBOptions& blob_db_options) : BlobDB(db), - db_impl_(dynamic_cast(db)), + db_impl_(static_cast_with_check(db)), opt_db_(new OptimisticTransactionDBImpl(db, false)), wo_set_(false), bdb_options_(blob_db_options), @@ -268,10 +265,9 @@ BlobDBImpl::BlobDBImpl(DB* db, const BlobDBOptions& blob_db_options) total_blob_space_(0) { assert(db_impl_ != nullptr); const BlobDBOptionsImpl* options_impl = - dynamic_cast(&blob_db_options); - if (options_impl) { - bdb_options_ = *options_impl; - } + static_cast_with_check( + &blob_db_options); + bdb_options_ = *options_impl; if (!bdb_options_.blob_dir.empty()) blob_dir_ = (bdb_options_.path_relative) @@ -1752,8 +1748,8 @@ Status BlobDBImpl::GCFileAndUpdateLSM(const std::shared_ptr& bfptr, gcstats->deleted_size += record.GetBlobSize(); if (first_gc) continue; - Transaction* txn = static_cast(opt_db_.get()) - ->BeginTransaction(write_options_); + Transaction* txn = opt_db_->BeginTransaction( + write_options_, OptimisticTransactionOptions(), nullptr); txn->Delete(cfh, record.Key()); Status s1 = txn->Commit(); // chances that this DELETE will fail is low. If it fails, it would be @@ -1817,8 +1813,8 @@ Status BlobDBImpl::GCFileAndUpdateLSM(const std::shared_ptr& bfptr, newfile->file_size_ += BlobLogRecord::kHeaderSize + record.Key().size() + record.Blob().size() + BlobLogRecord::kFooterSize; - Transaction* txn = static_cast(opt_db_.get()) - ->BeginTransaction(write_options_); + Transaction* txn = opt_db_->BeginTransaction( + write_options_, OptimisticTransactionOptions(), nullptr); txn->Put(cfh, record.Key(), index_entry); Status s1 = txn->Commit(); // chances that this Put will fail is low. If it fails, it would be because diff --git a/utilities/column_aware_encoding_util.cc b/utilities/column_aware_encoding_util.cc index a77d38d1d..c36e42254 100644 --- a/utilities/column_aware_encoding_util.cc +++ b/utilities/column_aware_encoding_util.cc @@ -51,11 +51,9 @@ void ColumnAwareEncodingReader::InitTableReader(const std::string& file_path) { options_.comparator = &internal_comparator_; options_.table_factory = std::make_shared(); - shared_ptr block_table_factory = - std::dynamic_pointer_cast(options_.table_factory); std::unique_ptr table_reader; - block_table_factory->NewTableReader( + options_.table_factory->NewTableReader( TableReaderOptions(ioptions_, soptions_, internal_comparator_, /*skip_filters=*/false), std::move(file_), file_size, &table_reader, /*enable_prefetch=*/false); diff --git a/utilities/options/options_util_test.cc b/utilities/options/options_util_test.cc index 86b382cfa..2ca8d4767 100644 --- a/utilities/options/options_util_test.cc +++ b/utilities/options/options_util_test.cc @@ -100,28 +100,34 @@ class DummyTableFactory : public TableFactory { DummyTableFactory() {} virtual ~DummyTableFactory() {} - virtual const char* Name() const { return "DummyTableFactory"; } + virtual const char* Name() const override { return "DummyTableFactory"; } - virtual Status NewTableReader(const TableReaderOptions& table_reader_options, - unique_ptr&& file, - uint64_t file_size, - unique_ptr* table_reader, - bool prefetch_index_and_filter_in_cache) const { + virtual Status NewTableReader( + const TableReaderOptions& table_reader_options, + unique_ptr&& file, uint64_t file_size, + unique_ptr* table_reader, + bool prefetch_index_and_filter_in_cache) const override { return Status::NotSupported(); } virtual TableBuilder* NewTableBuilder( const TableBuilderOptions& table_builder_options, - uint32_t column_family_id, WritableFileWriter* file) const { + uint32_t column_family_id, WritableFileWriter* file) const override { return nullptr; } - virtual Status SanitizeOptions(const DBOptions& db_opts, - const ColumnFamilyOptions& cf_opts) const { + virtual Status SanitizeOptions( + const DBOptions& db_opts, + const ColumnFamilyOptions& cf_opts) const override { return Status::NotSupported(); } - virtual std::string GetPrintableTableOptions() const { return ""; } + virtual std::string GetPrintableTableOptions() const override { return ""; } + + Status GetOptionString(std::string* opt_string, + const std::string& delimiter) const override { + return Status::OK(); + } }; class DummyMergeOperator : public MergeOperator { diff --git a/utilities/transactions/optimistic_transaction_impl.cc b/utilities/transactions/optimistic_transaction_impl.cc index 5652189bc..044dded23 100644 --- a/utilities/transactions/optimistic_transaction_impl.cc +++ b/utilities/transactions/optimistic_transaction_impl.cc @@ -17,6 +17,7 @@ #include "rocksdb/db.h" #include "rocksdb/status.h" #include "rocksdb/utilities/optimistic_transaction_db.h" +#include "util/cast_util.h" #include "util/string_util.h" #include "utilities/transactions/transaction_util.h" @@ -62,13 +63,7 @@ Status OptimisticTransactionImpl::Commit() { // check whether this transaction is safe to be committed. OptimisticTransactionCallback callback(this); - DBImpl* db_impl = dynamic_cast(db_->GetRootDB()); - if (db_impl == nullptr) { - // This should only happen if we support creating transactions from - // a StackableDB and someone overrides GetRootDB(). - return Status::InvalidArgument( - "DB::GetRootDB() returned an unexpected DB class"); - } + DBImpl* db_impl = static_cast_with_check(db_->GetRootDB()); Status s = db_impl->WriteWithCallback( write_options_, GetWriteBatch()->GetWriteBatch(), &callback); @@ -122,8 +117,7 @@ Status OptimisticTransactionImpl::TryLock(ColumnFamilyHandle* column_family, Status OptimisticTransactionImpl::CheckTransactionForConflicts(DB* db) { Status result; - assert(dynamic_cast(db) != nullptr); - auto db_impl = reinterpret_cast(db); + auto db_impl = static_cast_with_check(db); // Since we are on the write thread and do not want to block other writers, // we will do a cache-only conflict check. This can result in TryAgain diff --git a/utilities/transactions/transaction_db_impl.cc b/utilities/transactions/transaction_db_impl.cc index 2c425dd8d..69b5bc1ea 100644 --- a/utilities/transactions/transaction_db_impl.cc +++ b/utilities/transactions/transaction_db_impl.cc @@ -15,6 +15,7 @@ #include "rocksdb/db.h" #include "rocksdb/options.h" #include "rocksdb/utilities/transaction_db.h" +#include "util/cast_util.h" #include "utilities/transactions/transaction_db_mutex_impl.h" #include "utilities/transactions/transaction_impl.h" @@ -23,7 +24,7 @@ namespace rocksdb { TransactionDBImpl::TransactionDBImpl(DB* db, const TransactionDBOptions& txn_db_options) : TransactionDB(db), - db_impl_(dynamic_cast(db)), + db_impl_(static_cast_with_check(db)), txn_db_options_(txn_db_options), lock_mgr_(this, txn_db_options_.num_stripes, txn_db_options.max_num_locks, txn_db_options_.custom_mutex_factory @@ -52,7 +53,7 @@ TransactionDBImpl::TransactionDBImpl(DB* db, TransactionDBImpl::TransactionDBImpl(StackableDB* db, const TransactionDBOptions& txn_db_options) : TransactionDB(db), - db_impl_(dynamic_cast(db->GetRootDB())), + db_impl_(static_cast_with_check(db->GetRootDB())), txn_db_options_(txn_db_options), lock_mgr_(this, txn_db_options_.num_stripes, txn_db_options.max_num_locks, txn_db_options_.custom_mutex_factory @@ -371,8 +372,7 @@ Status TransactionDBImpl::Write(const WriteOptions& opts, WriteBatch* updates) { Transaction* txn = BeginInternalTransaction(opts); txn->DisableIndexing(); - assert(dynamic_cast(txn) != nullptr); - auto txn_impl = reinterpret_cast(txn); + auto txn_impl = static_cast_with_check(txn); // Since commitBatch sorts the keys before locking, concurrent Write() // operations will not cause a deadlock. @@ -412,8 +412,7 @@ bool TransactionDBImpl::TryStealingExpiredTransactionLocks( void TransactionDBImpl::ReinitializeTransaction( Transaction* txn, const WriteOptions& write_options, const TransactionOptions& txn_options) { - assert(dynamic_cast(txn) != nullptr); - auto txn_impl = reinterpret_cast(txn); + auto txn_impl = static_cast_with_check(txn); txn_impl->Reinitialize(this, write_options, txn_options); } diff --git a/utilities/transactions/transaction_impl.cc b/utilities/transactions/transaction_impl.cc index 408b15bcd..dd0c69be4 100644 --- a/utilities/transactions/transaction_impl.cc +++ b/utilities/transactions/transaction_impl.cc @@ -19,6 +19,7 @@ #include "rocksdb/snapshot.h" #include "rocksdb/status.h" #include "rocksdb/utilities/transaction_db.h" +#include "util/cast_util.h" #include "util/string_util.h" #include "util/sync_point.h" #include "utilities/transactions/transaction_db_impl.h" @@ -46,10 +47,9 @@ TransactionImpl::TransactionImpl(TransactionDB* txn_db, lock_timeout_(0), deadlock_detect_(false), deadlock_detect_depth_(0) { - txn_db_impl_ = dynamic_cast(txn_db); - assert(txn_db_impl_); - db_impl_ = dynamic_cast(txn_db->GetRootDB()); - assert(db_impl_); + txn_db_impl_ = + static_cast_with_check(txn_db); + db_impl_ = static_cast_with_check(txn_db->GetRootDB()); Initialize(txn_options); } @@ -526,8 +526,7 @@ Status TransactionImpl::ValidateSnapshot(ColumnFamilyHandle* column_family, *new_seqno = seq; - assert(dynamic_cast(db_) != nullptr); - auto db_impl = reinterpret_cast(db_); + auto db_impl = static_cast_with_check(db_); ColumnFamilyHandle* cfh = column_family ? column_family : db_impl->DefaultColumnFamily(); diff --git a/utilities/transactions/transaction_lock_mgr.cc b/utilities/transactions/transaction_lock_mgr.cc index a10aec17d..1184f667d 100644 --- a/utilities/transactions/transaction_lock_mgr.cc +++ b/utilities/transactions/transaction_lock_mgr.cc @@ -22,6 +22,7 @@ #include "rocksdb/slice.h" #include "rocksdb/utilities/transaction_db_mutex.h" +#include "util/cast_util.h" #include "util/murmurhash.h" #include "util/sync_point.h" #include "util/thread_local.h" @@ -112,8 +113,9 @@ TransactionLockMgr::TransactionLockMgr( max_num_locks_(max_num_locks), lock_maps_cache_(new ThreadLocalPtr(&UnrefLockMapsCache)), mutex_factory_(mutex_factory) { - txn_db_impl_ = dynamic_cast(txn_db); - assert(txn_db_impl_); + assert(txn_db); + txn_db_impl_ = + static_cast_with_check(txn_db); } TransactionLockMgr::~TransactionLockMgr() {}