// 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). // // 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. #include "table/block_based_table_factory.h" #include #include #include #include "options/options_helper.h" #include "port/port.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 { BlockBasedTableFactory::BlockBasedTableFactory( const BlockBasedTableOptions& _table_options) : table_options_(_table_options) { if (table_options_.flush_block_policy_factory == nullptr) { table_options_.flush_block_policy_factory.reset( new FlushBlockBySizePolicyFactory()); } if (table_options_.no_block_cache) { table_options_.block_cache.reset(); } else if (table_options_.block_cache == nullptr) { table_options_.block_cache = NewLRUCache(8 << 20); } if (table_options_.block_size_deviation < 0 || table_options_.block_size_deviation > 100) { table_options_.block_size_deviation = 0; } if (table_options_.block_restart_interval < 1) { table_options_.block_restart_interval = 1; } if (table_options_.index_block_restart_interval < 1) { table_options_.index_block_restart_interval = 1; } if (table_options_.partition_filters && table_options_.index_type != BlockBasedTableOptions::kTwoLevelIndexSearch) { // We do not support partitioned filters without partitioning indexes table_options_.partition_filters = false; } } Status BlockBasedTableFactory::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 { return BlockBasedTable::Open( table_reader_options.ioptions, table_reader_options.env_options, table_options_, table_reader_options.internal_comparator, std::move(file), file_size, table_reader, prefetch_index_and_filter_in_cache, table_reader_options.skip_filters, table_reader_options.level); } TableBuilder* BlockBasedTableFactory::NewTableBuilder( const TableBuilderOptions& table_builder_options, uint32_t column_family_id, WritableFileWriter* file) const { auto table_builder = new BlockBasedTableBuilder( table_builder_options.ioptions, table_options_, table_builder_options.internal_comparator, table_builder_options.int_tbl_prop_collector_factories, column_family_id, file, table_builder_options.compression_type, table_builder_options.compression_opts, table_builder_options.compression_dict, table_builder_options.skip_filters, table_builder_options.column_family_name, table_builder_options.creation_time); return table_builder; } Status BlockBasedTableFactory::SanitizeOptions( const DBOptions& db_opts, const ColumnFamilyOptions& cf_opts) const { if (table_options_.index_type == BlockBasedTableOptions::kHashSearch && cf_opts.prefix_extractor == nullptr) { return Status::InvalidArgument("Hash index is specified for block-based " "table, but prefix_extractor is not given"); } if (table_options_.cache_index_and_filter_blocks && table_options_.no_block_cache) { return Status::InvalidArgument("Enable cache_index_and_filter_blocks, " ", but block cache is disabled"); } if (table_options_.pin_l0_filter_and_index_blocks_in_cache && table_options_.no_block_cache) { return Status::InvalidArgument( "Enable pin_l0_filter_and_index_blocks_in_cache, " ", but block cache is disabled"); } if (!BlockBasedTableSupportedVersion(table_options_.format_version)) { return Status::InvalidArgument( "Unsupported BlockBasedTable format_version. Please check " "include/rocksdb/table.h for more info"); } return Status::OK(); } std::string BlockBasedTableFactory::GetPrintableTableOptions() const { std::string ret; ret.reserve(20000); const int kBufferSize = 200; char buffer[kBufferSize]; snprintf(buffer, kBufferSize, " flush_block_policy_factory: %s (%p)\n", table_options_.flush_block_policy_factory->Name(), static_cast(table_options_.flush_block_policy_factory.get())); ret.append(buffer); snprintf(buffer, kBufferSize, " cache_index_and_filter_blocks: %d\n", table_options_.cache_index_and_filter_blocks); ret.append(buffer); snprintf(buffer, kBufferSize, " cache_index_and_filter_blocks_with_high_priority: %d\n", table_options_.cache_index_and_filter_blocks_with_high_priority); ret.append(buffer); snprintf(buffer, kBufferSize, " pin_l0_filter_and_index_blocks_in_cache: %d\n", table_options_.pin_l0_filter_and_index_blocks_in_cache); ret.append(buffer); snprintf(buffer, kBufferSize, " index_type: %d\n", table_options_.index_type); ret.append(buffer); snprintf(buffer, kBufferSize, " hash_index_allow_collision: %d\n", table_options_.hash_index_allow_collision); ret.append(buffer); snprintf(buffer, kBufferSize, " checksum: %d\n", table_options_.checksum); ret.append(buffer); snprintf(buffer, kBufferSize, " no_block_cache: %d\n", table_options_.no_block_cache); ret.append(buffer); snprintf(buffer, kBufferSize, " block_cache: %p\n", static_cast(table_options_.block_cache.get())); ret.append(buffer); if (table_options_.block_cache) { const char* block_cache_name = table_options_.block_cache->Name(); if (block_cache_name != nullptr) { snprintf(buffer, kBufferSize, " block_cache_name: %s\n", block_cache_name); ret.append(buffer); } ret.append(" block_cache_options:\n"); ret.append(table_options_.block_cache->GetPrintableOptions()); } snprintf(buffer, kBufferSize, " block_cache_compressed: %p\n", static_cast(table_options_.block_cache_compressed.get())); ret.append(buffer); if (table_options_.block_cache_compressed) { const char* block_cache_compressed_name = table_options_.block_cache_compressed->Name(); if (block_cache_compressed_name != nullptr) { snprintf(buffer, kBufferSize, " block_cache_name: %s\n", block_cache_compressed_name); ret.append(buffer); } ret.append(" block_cache_compressed_options:\n"); ret.append(table_options_.block_cache_compressed->GetPrintableOptions()); } snprintf(buffer, kBufferSize, " persistent_cache: %p\n", static_cast(table_options_.persistent_cache.get())); ret.append(buffer); if (table_options_.persistent_cache) { snprintf(buffer, kBufferSize, " persistent_cache_options:\n"); ret.append(buffer); ret.append(table_options_.persistent_cache->GetPrintableOptions()); } snprintf(buffer, kBufferSize, " block_size: %" ROCKSDB_PRIszt "\n", table_options_.block_size); ret.append(buffer); snprintf(buffer, kBufferSize, " block_size_deviation: %d\n", table_options_.block_size_deviation); ret.append(buffer); snprintf(buffer, kBufferSize, " block_restart_interval: %d\n", table_options_.block_restart_interval); ret.append(buffer); snprintf(buffer, kBufferSize, " index_block_restart_interval: %d\n", table_options_.index_block_restart_interval); ret.append(buffer); snprintf(buffer, kBufferSize, " filter_policy: %s\n", table_options_.filter_policy == nullptr ? "nullptr" : table_options_.filter_policy->Name()); ret.append(buffer); snprintf(buffer, kBufferSize, " whole_key_filtering: %d\n", table_options_.whole_key_filtering); ret.append(buffer); snprintf(buffer, kBufferSize, " format_version: %d\n", table_options_.format_version); ret.append(buffer); 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 = "rocksdb.block.based.table.whole.key.filtering"; const std::string BlockBasedTablePropertyNames::kPrefixFiltering = "rocksdb.block.based.table.prefix.filtering"; const std::string kHashIndexPrefixesBlock = "rocksdb.hashindex.prefixes"; const std::string kHashIndexPrefixesMetadataBlock = "rocksdb.hashindex.metadata"; const std::string kPropTrue = "1"; const std::string kPropFalse = "0"; } // namespace rocksdb