1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 
10 #include <cctype>
11 #include <cinttypes>
12 #include <cstring>
13 #include <unordered_map>
14 
15 #include "cache/lru_cache.h"
16 #include "cache/sharded_cache.h"
17 #include "options/options_helper.h"
18 #include "options/options_parser.h"
19 #include "port/port.h"
20 #include "rocksdb/cache.h"
21 #include "rocksdb/convenience.h"
22 #include "rocksdb/memtablerep.h"
23 #include "rocksdb/utilities/leveldb_options.h"
24 #include "rocksdb/utilities/object_registry.h"
25 #include "rocksdb/utilities/options_type.h"
26 #include "table/block_based/filter_policy_internal.h"
27 #include "test_util/testharness.h"
28 #include "test_util/testutil.h"
29 #include "util/random.h"
30 #include "util/stderr_logger.h"
31 #include "util/string_util.h"
32 #include "utilities/merge_operators/bytesxor.h"
33 
34 #ifndef GFLAGS
35 bool FLAGS_enable_print = false;
36 #else
37 #include "util/gflags_compat.h"
38 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
39 DEFINE_bool(enable_print, false, "Print options generated to console.");
40 #endif  // GFLAGS
41 
42 namespace ROCKSDB_NAMESPACE {
43 
44 class OptionsTest : public testing::Test {};
45 
46 #ifndef ROCKSDB_LITE  // GetOptionsFromMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,GetOptionsFromMapTest)47 TEST_F(OptionsTest, GetOptionsFromMapTest) {
48   std::unordered_map<std::string, std::string> cf_options_map = {
49       {"write_buffer_size", "1"},
50       {"max_write_buffer_number", "2"},
51       {"min_write_buffer_number_to_merge", "3"},
52       {"max_write_buffer_number_to_maintain", "99"},
53       {"max_write_buffer_size_to_maintain", "-99999"},
54       {"compression", "kSnappyCompression"},
55       {"compression_per_level",
56        "kNoCompression:"
57        "kSnappyCompression:"
58        "kZlibCompression:"
59        "kBZip2Compression:"
60        "kLZ4Compression:"
61        "kLZ4HCCompression:"
62        "kXpressCompression:"
63        "kZSTD:"
64        "kZSTDNotFinalCompression"},
65       {"bottommost_compression", "kLZ4Compression"},
66       {"bottommost_compression_opts", "5:6:7:8:10:true"},
67       {"compression_opts", "4:5:6:7:8:true"},
68       {"num_levels", "8"},
69       {"level0_file_num_compaction_trigger", "8"},
70       {"level0_slowdown_writes_trigger", "9"},
71       {"level0_stop_writes_trigger", "10"},
72       {"target_file_size_base", "12"},
73       {"target_file_size_multiplier", "13"},
74       {"max_bytes_for_level_base", "14"},
75       {"level_compaction_dynamic_level_bytes", "true"},
76       {"max_bytes_for_level_multiplier", "15.0"},
77       {"max_bytes_for_level_multiplier_additional", "16:17:18"},
78       {"max_compaction_bytes", "21"},
79       {"soft_rate_limit", "1.1"},
80       {"hard_rate_limit", "2.1"},
81       {"hard_pending_compaction_bytes_limit", "211"},
82       {"arena_block_size", "22"},
83       {"disable_auto_compactions", "true"},
84       {"compaction_style", "kCompactionStyleLevel"},
85       {"compaction_pri", "kOldestSmallestSeqFirst"},
86       {"verify_checksums_in_compaction", "false"},
87       {"compaction_options_fifo", "23"},
88       {"max_sequential_skip_in_iterations", "24"},
89       {"inplace_update_support", "true"},
90       {"report_bg_io_stats", "true"},
91       {"compaction_measure_io_stats", "false"},
92       {"inplace_update_num_locks", "25"},
93       {"memtable_prefix_bloom_size_ratio", "0.26"},
94       {"memtable_whole_key_filtering", "true"},
95       {"memtable_huge_page_size", "28"},
96       {"bloom_locality", "29"},
97       {"max_successive_merges", "30"},
98       {"min_partial_merge_operands", "31"},
99       {"prefix_extractor", "fixed:31"},
100       {"optimize_filters_for_hits", "true"},
101       {"enable_blob_files", "true"},
102       {"min_blob_size", "1K"},
103       {"blob_file_size", "1G"},
104       {"blob_compression_type", "kZSTD"},
105       {"enable_blob_garbage_collection", "true"},
106       {"blob_garbage_collection_age_cutoff", "0.5"},
107   };
108 
109   std::unordered_map<std::string, std::string> db_options_map = {
110       {"create_if_missing", "false"},
111       {"create_missing_column_families", "true"},
112       {"error_if_exists", "false"},
113       {"paranoid_checks", "true"},
114       {"track_and_verify_wals_in_manifest", "true"},
115       {"max_open_files", "32"},
116       {"max_total_wal_size", "33"},
117       {"use_fsync", "true"},
118       {"db_log_dir", "/db_log_dir"},
119       {"wal_dir", "/wal_dir"},
120       {"delete_obsolete_files_period_micros", "34"},
121       {"max_background_compactions", "35"},
122       {"max_background_flushes", "36"},
123       {"max_log_file_size", "37"},
124       {"log_file_time_to_roll", "38"},
125       {"keep_log_file_num", "39"},
126       {"recycle_log_file_num", "5"},
127       {"max_manifest_file_size", "40"},
128       {"table_cache_numshardbits", "41"},
129       {"WAL_ttl_seconds", "43"},
130       {"WAL_size_limit_MB", "44"},
131       {"manifest_preallocation_size", "45"},
132       {"allow_mmap_reads", "true"},
133       {"allow_mmap_writes", "false"},
134       {"use_direct_reads", "false"},
135       {"use_direct_io_for_flush_and_compaction", "false"},
136       {"is_fd_close_on_exec", "true"},
137       {"skip_log_error_on_recovery", "false"},
138       {"stats_dump_period_sec", "46"},
139       {"stats_persist_period_sec", "57"},
140       {"persist_stats_to_disk", "false"},
141       {"stats_history_buffer_size", "69"},
142       {"advise_random_on_open", "true"},
143       {"use_adaptive_mutex", "false"},
144       {"new_table_reader_for_compaction_inputs", "true"},
145       {"compaction_readahead_size", "100"},
146       {"random_access_max_buffer_size", "3145728"},
147       {"writable_file_max_buffer_size", "314159"},
148       {"bytes_per_sync", "47"},
149       {"wal_bytes_per_sync", "48"},
150       {"strict_bytes_per_sync", "true"},
151   };
152 
153   ColumnFamilyOptions base_cf_opt;
154   ColumnFamilyOptions new_cf_opt;
155   ConfigOptions exact, loose;
156   exact.input_strings_escaped = false;
157   exact.ignore_unknown_options = false;
158   exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
159   loose.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
160 
161   loose.input_strings_escaped = false;
162   loose.ignore_unknown_options = true;
163   ASSERT_OK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
164                                           &new_cf_opt));
165   ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
166   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
167   ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
168   ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
169   ASSERT_EQ(new_cf_opt.max_write_buffer_size_to_maintain, -99999);
170   ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
171   ASSERT_EQ(new_cf_opt.compression_per_level.size(), 9U);
172   ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
173   ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
174   ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
175   ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
176   ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
177   ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
178   ASSERT_EQ(new_cf_opt.compression_per_level[6], kXpressCompression);
179   ASSERT_EQ(new_cf_opt.compression_per_level[7], kZSTD);
180   ASSERT_EQ(new_cf_opt.compression_per_level[8], kZSTDNotFinalCompression);
181   ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
182   ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
183   ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
184   ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
185   ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
186   ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
187             CompressionOptions().parallel_threads);
188   ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
189   ASSERT_EQ(new_cf_opt.bottommost_compression, kLZ4Compression);
190   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
191   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
192   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
193   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
194   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 10u);
195   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
196             CompressionOptions().parallel_threads);
197   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
198   ASSERT_EQ(new_cf_opt.num_levels, 8);
199   ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
200   ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
201   ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
202   ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
203   ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
204   ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
205   ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
206   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15.0);
207   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
208   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
209   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
210   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
211   ASSERT_EQ(new_cf_opt.max_compaction_bytes, 21);
212   ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
213   ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
214   ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
215   ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
216   ASSERT_EQ(new_cf_opt.compaction_pri, kOldestSmallestSeqFirst);
217   ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
218             static_cast<uint64_t>(23));
219   ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
220             static_cast<uint64_t>(24));
221   ASSERT_EQ(new_cf_opt.inplace_update_support, true);
222   ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
223   ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_size_ratio, 0.26);
224   ASSERT_EQ(new_cf_opt.memtable_whole_key_filtering, true);
225   ASSERT_EQ(new_cf_opt.memtable_huge_page_size, 28U);
226   ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
227   ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
228   ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
229   ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
230   ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
231             "rocksdb.FixedPrefix.31");
232   ASSERT_EQ(new_cf_opt.enable_blob_files, true);
233   ASSERT_EQ(new_cf_opt.min_blob_size, 1ULL << 10);
234   ASSERT_EQ(new_cf_opt.blob_file_size, 1ULL << 30);
235   ASSERT_EQ(new_cf_opt.blob_compression_type, kZSTD);
236   ASSERT_EQ(new_cf_opt.enable_blob_garbage_collection, true);
237   ASSERT_EQ(new_cf_opt.blob_garbage_collection_age_cutoff, 0.5);
238 
239   cf_options_map["write_buffer_size"] = "hello";
240   ASSERT_NOK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
241                                            &new_cf_opt));
242   ASSERT_OK(
243       RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
244 
245   cf_options_map["write_buffer_size"] = "1";
246   ASSERT_OK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
247                                           &new_cf_opt));
248 
249   cf_options_map["unknown_option"] = "1";
250   ASSERT_NOK(GetColumnFamilyOptionsFromMap(exact, base_cf_opt, cf_options_map,
251                                            &new_cf_opt));
252   ASSERT_OK(
253       RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
254 
255   // ignore_unknown_options=true;input_strings_escaped=false
256   ASSERT_OK(GetColumnFamilyOptionsFromMap(loose, base_cf_opt, cf_options_map,
257                                           &new_cf_opt));
258   ASSERT_OK(
259       RocksDBOptionsParser::VerifyCFOptions(loose, base_cf_opt, new_cf_opt));
260   ASSERT_NOK(
261       RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
262 
263   DBOptions base_db_opt;
264   DBOptions new_db_opt;
265   ASSERT_OK(
266       GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt));
267   ASSERT_EQ(new_db_opt.create_if_missing, false);
268   ASSERT_EQ(new_db_opt.create_missing_column_families, true);
269   ASSERT_EQ(new_db_opt.error_if_exists, false);
270   ASSERT_EQ(new_db_opt.paranoid_checks, true);
271   ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
272   ASSERT_EQ(new_db_opt.max_open_files, 32);
273   ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
274   ASSERT_EQ(new_db_opt.use_fsync, true);
275   ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
276   ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
277   ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
278             static_cast<uint64_t>(34));
279   ASSERT_EQ(new_db_opt.max_background_compactions, 35);
280   ASSERT_EQ(new_db_opt.max_background_flushes, 36);
281   ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
282   ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
283   ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
284   ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
285   ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
286   ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
287   ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
288   ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
289   ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
290   ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
291   ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
292   ASSERT_EQ(new_db_opt.use_direct_reads, false);
293   ASSERT_EQ(new_db_opt.use_direct_io_for_flush_and_compaction, false);
294   ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
295   ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
296   ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
297   ASSERT_EQ(new_db_opt.stats_persist_period_sec, 57U);
298   ASSERT_EQ(new_db_opt.persist_stats_to_disk, false);
299   ASSERT_EQ(new_db_opt.stats_history_buffer_size, 69U);
300   ASSERT_EQ(new_db_opt.advise_random_on_open, true);
301   ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
302   ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
303   ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
304   ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
305   ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
306   ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
307   ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
308   ASSERT_EQ(new_db_opt.strict_bytes_per_sync, true);
309 
310   db_options_map["max_open_files"] = "hello";
311   Status s =
312       GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt);
313   ASSERT_NOK(s);
314   ASSERT_TRUE(s.IsInvalidArgument());
315 
316   ASSERT_OK(
317       RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
318   ASSERT_OK(
319       RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
320 
321   // unknow options should fail parsing without ignore_unknown_options = true
322   db_options_map["unknown_db_option"] = "1";
323   s = GetDBOptionsFromMap(exact, base_db_opt, db_options_map, &new_db_opt);
324   ASSERT_NOK(s);
325   ASSERT_TRUE(s.IsInvalidArgument());
326   ASSERT_OK(
327       RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
328 
329   ASSERT_OK(
330       GetDBOptionsFromMap(loose, base_db_opt, db_options_map, &new_db_opt));
331   ASSERT_OK(
332       RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
333   ASSERT_NOK(
334       RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
335 }
336 #endif  // !ROCKSDB_LITE
337 
338 #ifndef ROCKSDB_LITE  // GetColumnFamilyOptionsFromString is not supported in
339                       // ROCKSDB_LITE
TEST_F(OptionsTest,GetColumnFamilyOptionsFromStringTest)340 TEST_F(OptionsTest, GetColumnFamilyOptionsFromStringTest) {
341   ColumnFamilyOptions base_cf_opt;
342   ColumnFamilyOptions new_cf_opt;
343   ConfigOptions config_options;
344   config_options.input_strings_escaped = false;
345   config_options.ignore_unknown_options = false;
346 
347   base_cf_opt.table_factory.reset();
348   ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt, "",
349                                              &new_cf_opt));
350   ASSERT_OK(GetColumnFamilyOptionsFromString(
351       config_options, base_cf_opt, "write_buffer_size=5", &new_cf_opt));
352   ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
353   ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
354   ASSERT_OK(GetColumnFamilyOptionsFromString(
355       config_options, base_cf_opt, "write_buffer_size=6;", &new_cf_opt));
356   ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
357   ASSERT_OK(GetColumnFamilyOptionsFromString(
358       config_options, base_cf_opt, "  write_buffer_size =  7  ", &new_cf_opt));
359   ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
360   ASSERT_OK(GetColumnFamilyOptionsFromString(
361       config_options, base_cf_opt, "  write_buffer_size =  8 ; ", &new_cf_opt));
362   ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
363   ASSERT_OK(GetColumnFamilyOptionsFromString(
364       config_options, base_cf_opt,
365       "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
366   ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
367   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
368   ASSERT_OK(GetColumnFamilyOptionsFromString(
369       config_options, base_cf_opt,
370       "write_buffer_size=11; max_write_buffer_number  =  12 ;", &new_cf_opt));
371   ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
372   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
373   // Wrong name "max_write_buffer_number_"
374   ASSERT_NOK(GetColumnFamilyOptionsFromString(
375       config_options, base_cf_opt,
376       "write_buffer_size=13;max_write_buffer_number_=14;", &new_cf_opt));
377   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
378                                                   new_cf_opt));
379 
380   // Comparator from object registry
381   std::string kCompName = "reverse_comp";
382   ObjectLibrary::Default()->Register<const Comparator>(
383       kCompName,
384       [](const std::string& /*name*/,
385          std::unique_ptr<const Comparator>* /*guard*/,
386          std::string* /* errmsg */) { return ReverseBytewiseComparator(); });
387 
388   ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
389                                              "comparator=" + kCompName + ";",
390                                              &new_cf_opt));
391   ASSERT_EQ(new_cf_opt.comparator, ReverseBytewiseComparator());
392 
393   // MergeOperator from object registry
394   std::unique_ptr<BytesXOROperator> bxo(new BytesXOROperator());
395   std::string kMoName = bxo->Name();
396   ObjectLibrary::Default()->Register<MergeOperator>(
397       kMoName,
398       [](const std::string& /*name*/, std::unique_ptr<MergeOperator>* guard,
399          std::string* /* errmsg */) {
400         guard->reset(new BytesXOROperator());
401         return guard->get();
402       });
403 
404   ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
405                                              "merge_operator=" + kMoName + ";",
406                                              &new_cf_opt));
407   ASSERT_EQ(kMoName, std::string(new_cf_opt.merge_operator->Name()));
408 
409   // Wrong key/value pair
410   Status s = GetColumnFamilyOptionsFromString(
411       config_options, base_cf_opt,
412       "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt);
413   ASSERT_NOK(s);
414   ASSERT_TRUE(s.IsInvalidArgument());
415 
416   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
417                                                   new_cf_opt));
418 
419   // Error Parsing value
420   s = GetColumnFamilyOptionsFromString(
421       config_options, base_cf_opt,
422       "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt);
423   ASSERT_NOK(s);
424   ASSERT_TRUE(s.IsInvalidArgument());
425   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
426                                                   new_cf_opt));
427 
428   // Missing option name
429   s = GetColumnFamilyOptionsFromString(
430       config_options, base_cf_opt, "write_buffer_size=13; =100;", &new_cf_opt);
431   ASSERT_NOK(s);
432   ASSERT_TRUE(s.IsInvalidArgument());
433   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
434                                                   new_cf_opt));
435 
436   const uint64_t kilo = 1024UL;
437   const uint64_t mega = 1024 * kilo;
438   const uint64_t giga = 1024 * mega;
439   const uint64_t tera = 1024 * giga;
440 
441   // Units (k)
442   ASSERT_OK(GetColumnFamilyOptionsFromString(
443       config_options, base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
444   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
445   // Units (m)
446   ASSERT_OK(GetColumnFamilyOptionsFromString(
447       config_options, base_cf_opt,
448       "max_write_buffer_number=16m;inplace_update_num_locks=17M", &new_cf_opt));
449   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
450   ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17u * mega);
451   // Units (g)
452   ASSERT_OK(GetColumnFamilyOptionsFromString(
453       config_options, base_cf_opt,
454       "write_buffer_size=18g;prefix_extractor=capped:8;"
455       "arena_block_size=19G",
456       &new_cf_opt));
457 
458   ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
459   ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
460   ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
461   std::string prefix_name(new_cf_opt.prefix_extractor->Name());
462   ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
463 
464   // Units (t)
465   ASSERT_OK(GetColumnFamilyOptionsFromString(
466       config_options, base_cf_opt, "write_buffer_size=20t;arena_block_size=21T",
467       &new_cf_opt));
468   ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
469   ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
470 
471   // Nested block based table options
472   // Empty
473   ASSERT_OK(GetColumnFamilyOptionsFromString(
474       config_options, base_cf_opt,
475       "write_buffer_size=10;max_write_buffer_number=16;"
476       "block_based_table_factory={};arena_block_size=1024",
477       &new_cf_opt));
478   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
479   // Non-empty
480   ASSERT_OK(GetColumnFamilyOptionsFromString(
481       config_options, base_cf_opt,
482       "write_buffer_size=10;max_write_buffer_number=16;"
483       "block_based_table_factory={block_cache=1M;block_size=4;};"
484       "arena_block_size=1024",
485       &new_cf_opt));
486   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
487   // Last one
488   ASSERT_OK(GetColumnFamilyOptionsFromString(
489       config_options, base_cf_opt,
490       "write_buffer_size=10;max_write_buffer_number=16;"
491       "block_based_table_factory={block_cache=1M;block_size=4;}",
492       &new_cf_opt));
493   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
494   // Mismatch curly braces
495   ASSERT_NOK(GetColumnFamilyOptionsFromString(
496       config_options, base_cf_opt,
497       "write_buffer_size=10;max_write_buffer_number=16;"
498       "block_based_table_factory={{{block_size=4;};"
499       "arena_block_size=1024",
500       &new_cf_opt));
501   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
502                                                   new_cf_opt));
503 
504   // Unexpected chars after closing curly brace
505   ASSERT_NOK(GetColumnFamilyOptionsFromString(
506       config_options, base_cf_opt,
507       "write_buffer_size=10;max_write_buffer_number=16;"
508       "block_based_table_factory={block_size=4;}};"
509       "arena_block_size=1024",
510       &new_cf_opt));
511   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
512                                                   new_cf_opt));
513 
514   ASSERT_NOK(GetColumnFamilyOptionsFromString(
515       config_options, base_cf_opt,
516       "write_buffer_size=10;max_write_buffer_number=16;"
517       "block_based_table_factory={block_size=4;}xdfa;"
518       "arena_block_size=1024",
519       &new_cf_opt));
520   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
521                                                   new_cf_opt));
522 
523   ASSERT_NOK(GetColumnFamilyOptionsFromString(
524       config_options, base_cf_opt,
525       "write_buffer_size=10;max_write_buffer_number=16;"
526       "block_based_table_factory={block_size=4;}xdfa",
527       &new_cf_opt));
528   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
529                                                   new_cf_opt));
530 
531   // Invalid block based table option
532   ASSERT_NOK(GetColumnFamilyOptionsFromString(
533       config_options, base_cf_opt,
534       "write_buffer_size=10;max_write_buffer_number=16;"
535       "block_based_table_factory={xx_block_size=4;}",
536       &new_cf_opt));
537   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
538                                                   new_cf_opt));
539 
540   ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
541                                              "optimize_filters_for_hits=true",
542                                              &new_cf_opt));
543   ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
544                                              "optimize_filters_for_hits=false",
545                                              &new_cf_opt));
546 
547   ASSERT_NOK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
548                                               "optimize_filters_for_hits=junk",
549                                               &new_cf_opt));
550   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opt,
551                                                   new_cf_opt));
552 
553   // Nested plain table options
554   // Empty
555   ASSERT_OK(GetColumnFamilyOptionsFromString(
556       config_options, base_cf_opt,
557       "write_buffer_size=10;max_write_buffer_number=16;"
558       "plain_table_factory={};arena_block_size=1024",
559       &new_cf_opt));
560   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
561   ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
562   // Non-empty
563   ASSERT_OK(GetColumnFamilyOptionsFromString(
564       config_options, base_cf_opt,
565       "write_buffer_size=10;max_write_buffer_number=16;"
566       "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
567       "arena_block_size=1024",
568       &new_cf_opt));
569   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
570   ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
571 
572   // memtable factory
573   ASSERT_OK(GetColumnFamilyOptionsFromString(
574       config_options, base_cf_opt,
575       "write_buffer_size=10;max_write_buffer_number=16;"
576       "memtable=skip_list:10;arena_block_size=1024",
577       &new_cf_opt));
578   ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
579   ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
580 }
581 
TEST_F(OptionsTest,CompressionOptionsFromString)582 TEST_F(OptionsTest, CompressionOptionsFromString) {
583   ColumnFamilyOptions base_cf_opt;
584   ColumnFamilyOptions new_cf_opt;
585   ConfigOptions config_options;
586   std::string opts_str;
587   config_options.ignore_unknown_options = false;
588   CompressionOptions dflt;
589   // Test with some optional values removed....
590   ASSERT_OK(
591       GetColumnFamilyOptionsFromString(config_options, ColumnFamilyOptions(),
592                                        "compression_opts=3:4:5; "
593                                        "bottommost_compression_opts=4:5:6:7",
594                                        &base_cf_opt));
595   ASSERT_EQ(base_cf_opt.compression_opts.window_bits, 3);
596   ASSERT_EQ(base_cf_opt.compression_opts.level, 4);
597   ASSERT_EQ(base_cf_opt.compression_opts.strategy, 5);
598   ASSERT_EQ(base_cf_opt.compression_opts.max_dict_bytes, dflt.max_dict_bytes);
599   ASSERT_EQ(base_cf_opt.compression_opts.zstd_max_train_bytes,
600             dflt.zstd_max_train_bytes);
601   ASSERT_EQ(base_cf_opt.compression_opts.parallel_threads,
602             dflt.parallel_threads);
603   ASSERT_EQ(base_cf_opt.compression_opts.enabled, dflt.enabled);
604   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.window_bits, 4);
605   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.level, 5);
606   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.strategy, 6);
607   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.max_dict_bytes, 7u);
608   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes,
609             dflt.zstd_max_train_bytes);
610   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.parallel_threads,
611             dflt.parallel_threads);
612   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.enabled, dflt.enabled);
613 
614   ASSERT_OK(GetColumnFamilyOptionsFromString(
615       config_options, ColumnFamilyOptions(),
616       "compression_opts=4:5:6:7:8:9:true; "
617       "bottommost_compression_opts=5:6:7:8:9:false",
618       &base_cf_opt));
619   ASSERT_EQ(base_cf_opt.compression_opts.window_bits, 4);
620   ASSERT_EQ(base_cf_opt.compression_opts.level, 5);
621   ASSERT_EQ(base_cf_opt.compression_opts.strategy, 6);
622   ASSERT_EQ(base_cf_opt.compression_opts.max_dict_bytes, 7u);
623   ASSERT_EQ(base_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
624   ASSERT_EQ(base_cf_opt.compression_opts.parallel_threads, 9u);
625   ASSERT_EQ(base_cf_opt.compression_opts.enabled, true);
626   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.window_bits, 5);
627   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.level, 6);
628   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.strategy, 7);
629   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
630   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
631   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.parallel_threads,
632             dflt.parallel_threads);
633   ASSERT_EQ(base_cf_opt.bottommost_compression_opts.enabled, false);
634 
635   ASSERT_OK(
636       GetStringFromColumnFamilyOptions(config_options, base_cf_opt, &opts_str));
637   ASSERT_OK(GetColumnFamilyOptionsFromString(
638       config_options, ColumnFamilyOptions(), opts_str, &new_cf_opt));
639   ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
640   ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
641   ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
642   ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
643   ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
644   ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads, 9u);
645   ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
646   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
647   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
648   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
649   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
650   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
651   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
652             dflt.parallel_threads);
653   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, false);
654 
655   // Test as struct values
656   ASSERT_OK(GetColumnFamilyOptionsFromString(
657       config_options, ColumnFamilyOptions(),
658       "compression_opts={window_bits=5; level=6; strategy=7; max_dict_bytes=8;"
659       "zstd_max_train_bytes=9;parallel_threads=10;enabled=true}; "
660       "bottommost_compression_opts={window_bits=4; level=5; strategy=6;"
661       " max_dict_bytes=7;zstd_max_train_bytes=8;parallel_threads=9;"
662       "enabled=false}; ",
663       &new_cf_opt));
664   ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 5);
665   ASSERT_EQ(new_cf_opt.compression_opts.level, 6);
666   ASSERT_EQ(new_cf_opt.compression_opts.strategy, 7);
667   ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 8u);
668   ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 9u);
669   ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads, 10u);
670   ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
671   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 4);
672   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 5);
673   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 6);
674   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 7u);
675   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 8u);
676   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads, 9u);
677   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, false);
678 
679   ASSERT_OK(GetColumnFamilyOptionsFromString(
680       config_options, base_cf_opt,
681       "compression_opts={window_bits=4; strategy=5;};"
682       "bottommost_compression_opts={level=6; strategy=7;}",
683       &new_cf_opt));
684   ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
685   ASSERT_EQ(new_cf_opt.compression_opts.strategy, 5);
686   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
687   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
688 
689   ASSERT_EQ(new_cf_opt.compression_opts.level,
690             base_cf_opt.compression_opts.level);
691   ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes,
692             base_cf_opt.compression_opts.max_dict_bytes);
693   ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes,
694             base_cf_opt.compression_opts.zstd_max_train_bytes);
695   ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
696             base_cf_opt.compression_opts.parallel_threads);
697   ASSERT_EQ(new_cf_opt.compression_opts.enabled,
698             base_cf_opt.compression_opts.enabled);
699   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits,
700             base_cf_opt.bottommost_compression_opts.window_bits);
701   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes,
702             base_cf_opt.bottommost_compression_opts.max_dict_bytes);
703   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes,
704             base_cf_opt.bottommost_compression_opts.zstd_max_train_bytes);
705   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
706             base_cf_opt.bottommost_compression_opts.parallel_threads);
707   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled,
708             base_cf_opt.bottommost_compression_opts.enabled);
709 
710   // Test a few individual struct values
711   ASSERT_OK(GetColumnFamilyOptionsFromString(
712       config_options, base_cf_opt,
713       "compression_opts.enabled=false; "
714       "bottommost_compression_opts.enabled=true; ",
715       &new_cf_opt));
716   ASSERT_EQ(new_cf_opt.compression_opts.enabled, false);
717   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
718 
719   // Now test some illegal values
720   ConfigOptions ignore;
721   ignore.ignore_unknown_options = true;
722   ASSERT_NOK(GetColumnFamilyOptionsFromString(
723       config_options, ColumnFamilyOptions(),
724       "compression_opts=5:6:7:8:9:x:false", &base_cf_opt));
725   ASSERT_OK(GetColumnFamilyOptionsFromString(
726       ignore, ColumnFamilyOptions(), "compression_opts=5:6:7:8:9:x:false",
727       &base_cf_opt));
728   ASSERT_OK(GetColumnFamilyOptionsFromString(
729       config_options, ColumnFamilyOptions(),
730       "compression_opts=1:2:3:4:5:6:true:8", &base_cf_opt));
731   ASSERT_OK(GetColumnFamilyOptionsFromString(
732       ignore, ColumnFamilyOptions(), "compression_opts=1:2:3:4:5:6:true:8",
733       &base_cf_opt));
734   ASSERT_NOK(GetColumnFamilyOptionsFromString(
735       config_options, ColumnFamilyOptions(),
736       "compression_opts=1:2:3:4:5:6:true:8:9", &base_cf_opt));
737   ASSERT_OK(GetColumnFamilyOptionsFromString(
738       ignore, ColumnFamilyOptions(), "compression_opts=1:2:3:4:5:6:true:8:9",
739       &base_cf_opt));
740   ASSERT_NOK(GetColumnFamilyOptionsFromString(
741       config_options, ColumnFamilyOptions(), "compression_opts={unknown=bad;}",
742       &base_cf_opt));
743   ASSERT_OK(GetColumnFamilyOptionsFromString(ignore, ColumnFamilyOptions(),
744                                              "compression_opts={unknown=bad;}",
745                                              &base_cf_opt));
746   ASSERT_NOK(GetColumnFamilyOptionsFromString(
747       config_options, ColumnFamilyOptions(), "compression_opts.unknown=bad",
748       &base_cf_opt));
749   ASSERT_OK(GetColumnFamilyOptionsFromString(ignore, ColumnFamilyOptions(),
750                                              "compression_opts.unknown=bad",
751                                              &base_cf_opt));
752 }
753 
TEST_F(OptionsTest,OldInterfaceTest)754 TEST_F(OptionsTest, OldInterfaceTest) {
755   ColumnFamilyOptions base_cf_opt;
756   ColumnFamilyOptions new_cf_opt;
757   ConfigOptions exact;
758 
759   ASSERT_OK(GetColumnFamilyOptionsFromString(
760       base_cf_opt,
761       "write_buffer_size=18;prefix_extractor=capped:8;"
762       "arena_block_size=19",
763       &new_cf_opt));
764 
765   ASSERT_EQ(new_cf_opt.write_buffer_size, 18);
766   ASSERT_EQ(new_cf_opt.arena_block_size, 19);
767   ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
768 
769   // And with a bad option
770   ASSERT_NOK(GetColumnFamilyOptionsFromString(
771       base_cf_opt,
772       "write_buffer_size=10;max_write_buffer_number=16;"
773       "block_based_table_factory={xx_block_size=4;}",
774       &new_cf_opt));
775   ASSERT_OK(
776       RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
777 
778   std::unordered_map<std::string, std::string> cf_options_map = {
779       {"write_buffer_size", "1"},
780       {"max_write_buffer_number", "2"},
781       {"min_write_buffer_number_to_merge", "3"},
782   };
783   ASSERT_OK(
784       GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
785   cf_options_map["unknown_option"] = "1";
786   ASSERT_NOK(
787       GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
788   ASSERT_OK(
789       RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
790   ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
791                                           &new_cf_opt, true, true));
792 
793   DBOptions base_db_opt;
794   DBOptions new_db_opt;
795   std::unordered_map<std::string, std::string> db_options_map = {
796       {"create_if_missing", "false"},
797       {"create_missing_column_families", "true"},
798       {"error_if_exists", "false"},
799       {"paranoid_checks", "true"},
800       {"track_and_verify_wals_in_manifest", "true"},
801       {"max_open_files", "32"},
802   };
803   ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
804   ASSERT_EQ(new_db_opt.create_if_missing, false);
805   ASSERT_EQ(new_db_opt.create_missing_column_families, true);
806   ASSERT_EQ(new_db_opt.error_if_exists, false);
807   ASSERT_EQ(new_db_opt.paranoid_checks, true);
808   ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
809   ASSERT_EQ(new_db_opt.max_open_files, 32);
810   db_options_map["unknown_option"] = "1";
811   Status s = GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt);
812   ASSERT_NOK(s);
813   ASSERT_TRUE(s.IsInvalidArgument());
814 
815   ASSERT_OK(
816       RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
817   ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt, true,
818                                 true));
819   ASSERT_OK(GetDBOptionsFromString(
820       base_db_opt,
821       "create_if_missing=false;error_if_exists=false;max_open_files=42;",
822       &new_db_opt));
823   ASSERT_EQ(new_db_opt.create_if_missing, false);
824   ASSERT_EQ(new_db_opt.error_if_exists, false);
825   ASSERT_EQ(new_db_opt.max_open_files, 42);
826   s = GetDBOptionsFromString(
827       base_db_opt,
828       "create_if_missing=false;error_if_exists=false;max_open_files=42;"
829       "unknown_option=1;",
830       &new_db_opt);
831   ASSERT_NOK(s);
832   ASSERT_TRUE(s.IsInvalidArgument());
833   ASSERT_OK(
834       RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
835 }
836 
837 #endif  // !ROCKSDB_LITE
838 
839 #ifndef ROCKSDB_LITE  // GetBlockBasedTableOptionsFromString is not supported
TEST_F(OptionsTest,GetBlockBasedTableOptionsFromString)840 TEST_F(OptionsTest, GetBlockBasedTableOptionsFromString) {
841   BlockBasedTableOptions table_opt;
842   BlockBasedTableOptions new_opt;
843   ConfigOptions config_options;
844   config_options.input_strings_escaped = false;
845   config_options.ignore_unknown_options = false;
846 
847   // make sure default values are overwritten by something else
848   ASSERT_OK(GetBlockBasedTableOptionsFromString(
849       config_options, table_opt,
850       "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
851       "checksum=kxxHash;hash_index_allow_collision=1;"
852       "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
853       "block_size_deviation=8;block_restart_interval=4;"
854       "format_version=5;whole_key_filtering=1;"
855       "filter_policy=bloomfilter:4.567:false;"
856       // A bug caused read_amp_bytes_per_bit to be a large integer in OPTIONS
857       // file generated by 6.10 to 6.14. Though bug is fixed in these releases,
858       // we need to handle the case of loading OPTIONS file generated before the
859       // fix.
860       "read_amp_bytes_per_bit=17179869185;",
861       &new_opt));
862   ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
863   ASSERT_EQ(new_opt.index_type, BlockBasedTableOptions::kHashSearch);
864   ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
865   ASSERT_TRUE(new_opt.hash_index_allow_collision);
866   ASSERT_TRUE(new_opt.block_cache != nullptr);
867   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
868   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
869   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
870   ASSERT_EQ(new_opt.block_size, 1024UL);
871   ASSERT_EQ(new_opt.block_size_deviation, 8);
872   ASSERT_EQ(new_opt.block_restart_interval, 4);
873   ASSERT_EQ(new_opt.format_version, 5U);
874   ASSERT_EQ(new_opt.whole_key_filtering, true);
875   ASSERT_TRUE(new_opt.filter_policy != nullptr);
876   const BloomFilterPolicy* bfp =
877       dynamic_cast<const BloomFilterPolicy*>(new_opt.filter_policy.get());
878   EXPECT_EQ(bfp->GetMillibitsPerKey(), 4567);
879   EXPECT_EQ(bfp->GetWholeBitsPerKey(), 5);
880   EXPECT_EQ(bfp->GetMode(), BloomFilterPolicy::kAutoBloom);
881   // Verify that only the lower 32bits are stored in
882   // new_opt.read_amp_bytes_per_bit.
883   EXPECT_EQ(1U, new_opt.read_amp_bytes_per_bit);
884 
885   // unknown option
886   Status s = GetBlockBasedTableOptionsFromString(
887       config_options, table_opt,
888       "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
889       "bad_option=1",
890       &new_opt);
891   ASSERT_NOK(s);
892   ASSERT_TRUE(s.IsInvalidArgument());
893   ASSERT_EQ(static_cast<bool>(table_opt.cache_index_and_filter_blocks),
894             new_opt.cache_index_and_filter_blocks);
895   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
896 
897   // unrecognized index type
898   s = GetBlockBasedTableOptionsFromString(
899       config_options, table_opt,
900       "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX", &new_opt);
901   ASSERT_NOK(s);
902   ASSERT_TRUE(s.IsInvalidArgument());
903   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
904             new_opt.cache_index_and_filter_blocks);
905   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
906 
907   // unrecognized checksum type
908   ASSERT_NOK(GetBlockBasedTableOptionsFromString(
909       config_options, table_opt,
910       "cache_index_and_filter_blocks=1;checksum=kxxHashXX", &new_opt));
911   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
912             new_opt.cache_index_and_filter_blocks);
913   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
914 
915   // unrecognized filter policy name
916   s = GetBlockBasedTableOptionsFromString(config_options, table_opt,
917                                           "cache_index_and_filter_blocks=1;"
918                                           "filter_policy=bloomfilterxx:4:true",
919                                           &new_opt);
920   ASSERT_NOK(s);
921   ASSERT_TRUE(s.IsInvalidArgument());
922   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
923             new_opt.cache_index_and_filter_blocks);
924   ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
925 
926   // unrecognized filter policy config
927   s = GetBlockBasedTableOptionsFromString(config_options, table_opt,
928                                           "cache_index_and_filter_blocks=1;"
929                                           "filter_policy=bloomfilter:4",
930                                           &new_opt);
931   ASSERT_NOK(s);
932   ASSERT_TRUE(s.IsInvalidArgument());
933   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
934             new_opt.cache_index_and_filter_blocks);
935   ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
936 
937   // Experimental Ribbon filter policy
938   ASSERT_OK(GetBlockBasedTableOptionsFromString(
939       config_options, table_opt, "filter_policy=experimental_ribbon:5.678;",
940       &new_opt));
941   ASSERT_TRUE(new_opt.filter_policy != nullptr);
942   bfp = dynamic_cast<const BloomFilterPolicy*>(new_opt.filter_policy.get());
943   EXPECT_EQ(bfp->GetMillibitsPerKey(), 5678);
944   EXPECT_EQ(bfp->GetMode(), BloomFilterPolicy::kStandard128Ribbon);
945 
946   // Check block cache options are overwritten when specified
947   // in new format as a struct.
948   ASSERT_OK(GetBlockBasedTableOptionsFromString(
949       config_options, table_opt,
950       "block_cache={capacity=1M;num_shard_bits=4;"
951       "strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
952       "block_cache_compressed={capacity=1M;num_shard_bits=4;"
953       "strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
954       &new_opt));
955   ASSERT_TRUE(new_opt.block_cache != nullptr);
956   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
957   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
958                 new_opt.block_cache)->GetNumShardBits(), 4);
959   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
960   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
961                 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
962   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
963   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
964   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
965                 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
966   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
967   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
968                 new_opt.block_cache_compressed)->GetHighPriPoolRatio(),
969                 0.5);
970 
971   // Set only block cache capacity. Check other values are
972   // reset to default values.
973   ASSERT_OK(GetBlockBasedTableOptionsFromString(
974       config_options, table_opt,
975       "block_cache={capacity=2M};"
976       "block_cache_compressed={capacity=2M}",
977       &new_opt));
978   ASSERT_TRUE(new_opt.block_cache != nullptr);
979   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL);
980   // Default values
981   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
982                 new_opt.block_cache)->GetNumShardBits(),
983                 GetDefaultCacheShardBits(new_opt.block_cache->GetCapacity()));
984   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
985   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
986                 ->GetHighPriPoolRatio(),
987             0.5);
988   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
989   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 2*1024UL*1024UL);
990   // Default values
991   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
992                 new_opt.block_cache_compressed)->GetNumShardBits(),
993                 GetDefaultCacheShardBits(
994                     new_opt.block_cache_compressed->GetCapacity()));
995   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
996   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
997                 ->GetHighPriPoolRatio(),
998             0.5);
999 
1000   // Set couple of block cache options.
1001   ASSERT_OK(GetBlockBasedTableOptionsFromString(
1002       config_options, table_opt,
1003       "block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};"
1004       "block_cache_compressed={num_shard_bits=5;"
1005       "high_pri_pool_ratio=0.0;}",
1006       &new_opt));
1007   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 0);
1008   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1009                 new_opt.block_cache)->GetNumShardBits(), 5);
1010   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
1011   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
1012                 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
1013   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
1014   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 0);
1015   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1016                 new_opt.block_cache_compressed)->GetNumShardBits(), 5);
1017   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
1018   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
1019                 ->GetHighPriPoolRatio(),
1020             0.0);
1021 
1022   // Set couple of block cache options.
1023   ASSERT_OK(GetBlockBasedTableOptionsFromString(
1024       config_options, table_opt,
1025       "block_cache={capacity=1M;num_shard_bits=4;"
1026       "strict_capacity_limit=true;};"
1027       "block_cache_compressed={capacity=1M;num_shard_bits=4;"
1028       "strict_capacity_limit=true;}",
1029       &new_opt));
1030   ASSERT_TRUE(new_opt.block_cache != nullptr);
1031   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
1032   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1033                 new_opt.block_cache)->GetNumShardBits(), 4);
1034   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
1035   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
1036                 ->GetHighPriPoolRatio(),
1037             0.5);
1038   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
1039   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
1040   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
1041                 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
1042   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
1043   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
1044                 ->GetHighPriPoolRatio(),
1045             0.5);
1046 }
1047 #endif  // !ROCKSDB_LITE
1048 
1049 
1050 #ifndef ROCKSDB_LITE  // GetPlainTableOptionsFromString is not supported
TEST_F(OptionsTest,GetPlainTableOptionsFromString)1051 TEST_F(OptionsTest, GetPlainTableOptionsFromString) {
1052   PlainTableOptions table_opt;
1053   PlainTableOptions new_opt;
1054   ConfigOptions config_options;
1055   config_options.input_strings_escaped = false;
1056   config_options.ignore_unknown_options = false;
1057   // make sure default values are overwritten by something else
1058   ASSERT_OK(GetPlainTableOptionsFromString(
1059       config_options, table_opt,
1060       "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1061       "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
1062       "full_scan_mode=true;store_index_in_file=true",
1063       &new_opt));
1064   ASSERT_EQ(new_opt.user_key_len, 66u);
1065   ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
1066   ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
1067   ASSERT_EQ(new_opt.index_sparseness, 8);
1068   ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
1069   ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
1070   ASSERT_TRUE(new_opt.full_scan_mode);
1071   ASSERT_TRUE(new_opt.store_index_in_file);
1072 
1073   // unknown option
1074   Status s = GetPlainTableOptionsFromString(
1075       config_options, table_opt,
1076       "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1077       "bad_option=1",
1078       &new_opt);
1079   ASSERT_NOK(s);
1080   ASSERT_TRUE(s.IsInvalidArgument());
1081 
1082   // unrecognized EncodingType
1083   s = GetPlainTableOptionsFromString(
1084       config_options, table_opt,
1085       "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
1086       "encoding_type=kPrefixXX",
1087       &new_opt);
1088   ASSERT_NOK(s);
1089   ASSERT_TRUE(s.IsInvalidArgument());
1090 }
1091 #endif  // !ROCKSDB_LITE
1092 
1093 #ifndef ROCKSDB_LITE  // GetMemTableRepFactoryFromString is not supported
TEST_F(OptionsTest,GetMemTableRepFactoryFromString)1094 TEST_F(OptionsTest, GetMemTableRepFactoryFromString) {
1095   std::unique_ptr<MemTableRepFactory> new_mem_factory = nullptr;
1096 
1097   ASSERT_OK(GetMemTableRepFactoryFromString("skip_list", &new_mem_factory));
1098   ASSERT_OK(GetMemTableRepFactoryFromString("skip_list:16", &new_mem_factory));
1099   ASSERT_EQ(std::string(new_mem_factory->Name()), "SkipListFactory");
1100   ASSERT_NOK(GetMemTableRepFactoryFromString("skip_list:16:invalid_opt",
1101                                              &new_mem_factory));
1102 
1103   ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash", &new_mem_factory));
1104   ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash:1000",
1105                                             &new_mem_factory));
1106   ASSERT_EQ(std::string(new_mem_factory->Name()), "HashSkipListRepFactory");
1107   ASSERT_NOK(GetMemTableRepFactoryFromString("prefix_hash:1000:invalid_opt",
1108                                              &new_mem_factory));
1109 
1110   ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist",
1111                                             &new_mem_factory));
1112   ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist:1000",
1113                                             &new_mem_factory));
1114   ASSERT_EQ(std::string(new_mem_factory->Name()), "HashLinkListRepFactory");
1115   ASSERT_NOK(GetMemTableRepFactoryFromString("hash_linkedlist:1000:invalid_opt",
1116                                              &new_mem_factory));
1117 
1118   ASSERT_OK(GetMemTableRepFactoryFromString("vector", &new_mem_factory));
1119   ASSERT_OK(GetMemTableRepFactoryFromString("vector:1024", &new_mem_factory));
1120   ASSERT_EQ(std::string(new_mem_factory->Name()), "VectorRepFactory");
1121   ASSERT_NOK(GetMemTableRepFactoryFromString("vector:1024:invalid_opt",
1122                                              &new_mem_factory));
1123 
1124   ASSERT_NOK(GetMemTableRepFactoryFromString("cuckoo", &new_mem_factory));
1125   // CuckooHash memtable is already removed.
1126   ASSERT_NOK(GetMemTableRepFactoryFromString("cuckoo:1024", &new_mem_factory));
1127 
1128   ASSERT_NOK(GetMemTableRepFactoryFromString("bad_factory", &new_mem_factory));
1129 }
1130 #endif  // !ROCKSDB_LITE
1131 
1132 #ifndef ROCKSDB_LITE  // GetOptionsFromString is not supported in RocksDB Lite
TEST_F(OptionsTest,GetOptionsFromStringTest)1133 TEST_F(OptionsTest, GetOptionsFromStringTest) {
1134   Options base_options, new_options;
1135   ConfigOptions config_options;
1136   config_options.input_strings_escaped = false;
1137   config_options.ignore_unknown_options = false;
1138 
1139   base_options.write_buffer_size = 20;
1140   base_options.min_write_buffer_number_to_merge = 15;
1141   BlockBasedTableOptions block_based_table_options;
1142   block_based_table_options.cache_index_and_filter_blocks = true;
1143   base_options.table_factory.reset(
1144       NewBlockBasedTableFactory(block_based_table_options));
1145 
1146   // Register an Env with object registry.
1147   const static char* kCustomEnvName = "CustomEnv";
1148   class CustomEnv : public EnvWrapper {
1149    public:
1150     explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
1151   };
1152 
1153   ObjectLibrary::Default()->Register<Env>(
1154       kCustomEnvName,
1155       [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
1156          std::string* /* errmsg */) {
1157         static CustomEnv env(Env::Default());
1158         return &env;
1159       });
1160 
1161   ASSERT_OK(GetOptionsFromString(
1162       config_options, base_options,
1163       "write_buffer_size=10;max_write_buffer_number=16;"
1164       "block_based_table_factory={block_cache=1M;block_size=4;};"
1165       "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
1166       "bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files="
1167       "1;"
1168       "rate_limiter_bytes_per_sec=1024;env=CustomEnv",
1169       &new_options));
1170 
1171   ASSERT_EQ(new_options.compression_opts.window_bits, 4);
1172   ASSERT_EQ(new_options.compression_opts.level, 5);
1173   ASSERT_EQ(new_options.compression_opts.strategy, 6);
1174   ASSERT_EQ(new_options.compression_opts.max_dict_bytes, 0u);
1175   ASSERT_EQ(new_options.compression_opts.zstd_max_train_bytes, 0u);
1176   ASSERT_EQ(new_options.compression_opts.parallel_threads, 1u);
1177   ASSERT_EQ(new_options.compression_opts.enabled, false);
1178   ASSERT_EQ(new_options.bottommost_compression, kDisableCompressionOption);
1179   ASSERT_EQ(new_options.bottommost_compression_opts.window_bits, 5);
1180   ASSERT_EQ(new_options.bottommost_compression_opts.level, 6);
1181   ASSERT_EQ(new_options.bottommost_compression_opts.strategy, 7);
1182   ASSERT_EQ(new_options.bottommost_compression_opts.max_dict_bytes, 0u);
1183   ASSERT_EQ(new_options.bottommost_compression_opts.zstd_max_train_bytes, 0u);
1184   ASSERT_EQ(new_options.bottommost_compression_opts.parallel_threads, 1u);
1185   ASSERT_EQ(new_options.bottommost_compression_opts.enabled, false);
1186   ASSERT_EQ(new_options.write_buffer_size, 10U);
1187   ASSERT_EQ(new_options.max_write_buffer_number, 16);
1188   const auto new_bbto =
1189       new_options.table_factory->GetOptions<BlockBasedTableOptions>();
1190   ASSERT_NE(new_bbto, nullptr);
1191   ASSERT_EQ(new_bbto->block_cache->GetCapacity(), 1U << 20);
1192   ASSERT_EQ(new_bbto->block_size, 4U);
1193   // don't overwrite block based table options
1194   ASSERT_TRUE(new_bbto->cache_index_and_filter_blocks);
1195 
1196   ASSERT_EQ(new_options.create_if_missing, true);
1197   ASSERT_EQ(new_options.max_open_files, 1);
1198   ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
1199   Env* newEnv = new_options.env;
1200   ASSERT_OK(Env::LoadEnv(kCustomEnvName, &newEnv));
1201   ASSERT_EQ(newEnv, new_options.env);
1202 
1203   config_options.ignore_unknown_options = false;
1204   // Test a bad value for a DBOption returns a failure
1205   base_options.dump_malloc_stats = false;
1206   base_options.write_buffer_size = 1024;
1207   Options bad_options = new_options;
1208   Status s = GetOptionsFromString(config_options, base_options,
1209                                   "create_if_missing=XX;dump_malloc_stats=true",
1210                                   &bad_options);
1211   ASSERT_NOK(s);
1212   ASSERT_TRUE(s.IsInvalidArgument());
1213   ASSERT_EQ(bad_options.dump_malloc_stats, false);
1214 
1215   bad_options = new_options;
1216   s = GetOptionsFromString(config_options, base_options,
1217                            "write_buffer_size=XX;dump_malloc_stats=true",
1218                            &bad_options);
1219   ASSERT_NOK(s);
1220   ASSERT_TRUE(s.IsInvalidArgument());
1221 
1222   ASSERT_EQ(bad_options.dump_malloc_stats, false);
1223 
1224   // Test a bad value for a TableFactory Option returns a failure
1225   bad_options = new_options;
1226   s = GetOptionsFromString(config_options, base_options,
1227                            "write_buffer_size=16;dump_malloc_stats=true"
1228                            "block_based_table_factory={block_size=XX;};",
1229                            &bad_options);
1230   ASSERT_TRUE(s.IsInvalidArgument());
1231   ASSERT_EQ(bad_options.dump_malloc_stats, false);
1232   ASSERT_EQ(bad_options.write_buffer_size, 1024);
1233 
1234   config_options.ignore_unknown_options = true;
1235   ASSERT_OK(GetOptionsFromString(config_options, base_options,
1236                                  "create_if_missing=XX;dump_malloc_stats=true;"
1237                                  "write_buffer_size=XX;"
1238                                  "block_based_table_factory={block_size=XX;};",
1239                                  &bad_options));
1240   ASSERT_EQ(bad_options.create_if_missing, base_options.create_if_missing);
1241   ASSERT_EQ(bad_options.dump_malloc_stats, true);
1242   ASSERT_EQ(bad_options.write_buffer_size, base_options.write_buffer_size);
1243 
1244   // Test the old interface
1245   ASSERT_OK(GetOptionsFromString(
1246       base_options,
1247       "write_buffer_size=22;max_write_buffer_number=33;max_open_files=44;",
1248       &new_options));
1249   ASSERT_EQ(new_options.write_buffer_size, 22U);
1250   ASSERT_EQ(new_options.max_write_buffer_number, 33);
1251   ASSERT_EQ(new_options.max_open_files, 44);
1252 }
1253 
TEST_F(OptionsTest,DBOptionsSerialization)1254 TEST_F(OptionsTest, DBOptionsSerialization) {
1255   Options base_options, new_options;
1256   Random rnd(301);
1257   ConfigOptions config_options;
1258   config_options.input_strings_escaped = false;
1259   config_options.ignore_unknown_options = false;
1260 
1261   // Phase 1: Make big change in base_options
1262   test::RandomInitDBOptions(&base_options, &rnd);
1263 
1264   // Phase 2: obtain a string from base_option
1265   std::string base_options_file_content;
1266   ASSERT_OK(GetStringFromDBOptions(config_options, base_options,
1267                                    &base_options_file_content));
1268 
1269   // Phase 3: Set new_options from the derived string and expect
1270   //          new_options == base_options
1271   ASSERT_OK(GetDBOptionsFromString(config_options, DBOptions(),
1272                                    base_options_file_content, &new_options));
1273   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_options,
1274                                                   new_options));
1275 }
1276 
TEST_F(OptionsTest,OptionsComposeDecompose)1277 TEST_F(OptionsTest, OptionsComposeDecompose) {
1278   // build an Options from DBOptions + CFOptions, then decompose it to verify
1279   // we get same constituent options.
1280   DBOptions base_db_opts;
1281   ColumnFamilyOptions base_cf_opts;
1282   ConfigOptions
1283       config_options;  // Use default for ignore(false) and check (exact)
1284   config_options.input_strings_escaped = false;
1285 
1286   Random rnd(301);
1287   test::RandomInitDBOptions(&base_db_opts, &rnd);
1288   test::RandomInitCFOptions(&base_cf_opts, base_db_opts, &rnd);
1289 
1290   Options base_opts(base_db_opts, base_cf_opts);
1291   DBOptions new_db_opts(base_opts);
1292   ColumnFamilyOptions new_cf_opts(base_opts);
1293 
1294   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_db_opts,
1295                                                   new_db_opts));
1296   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_cf_opts,
1297                                                   new_cf_opts));
1298   delete new_cf_opts.compaction_filter;
1299 }
1300 
TEST_F(OptionsTest,DBOptionsComposeImmutable)1301 TEST_F(OptionsTest, DBOptionsComposeImmutable) {
1302   // Build a DBOptions from an Immutable/Mutable one and verify that
1303   // we get same constituent options.
1304   ConfigOptions config_options;
1305   Random rnd(301);
1306   DBOptions base_opts, new_opts;
1307   test::RandomInitDBOptions(&base_opts, &rnd);
1308   MutableDBOptions m_opts(base_opts);
1309   ImmutableDBOptions i_opts(base_opts);
1310   new_opts = BuildDBOptions(i_opts, m_opts);
1311   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_opts,
1312                                                   new_opts));
1313 }
1314 
TEST_F(OptionsTest,GetMutableDBOptions)1315 TEST_F(OptionsTest, GetMutableDBOptions) {
1316   Random rnd(228);
1317   DBOptions base_opts;
1318   std::string opts_str;
1319   std::unordered_map<std::string, std::string> opts_map;
1320   ConfigOptions config_options;
1321 
1322   test::RandomInitDBOptions(&base_opts, &rnd);
1323   ImmutableDBOptions i_opts(base_opts);
1324   MutableDBOptions m_opts(base_opts);
1325   MutableDBOptions new_opts;
1326   ASSERT_OK(GetStringFromMutableDBOptions(config_options, m_opts, &opts_str));
1327   ASSERT_OK(StringToMap(opts_str, &opts_map));
1328   ASSERT_OK(GetMutableDBOptionsFromStrings(m_opts, opts_map, &new_opts));
1329   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(
1330       config_options, base_opts, BuildDBOptions(i_opts, new_opts)));
1331 }
1332 
TEST_F(OptionsTest,CFOptionsComposeImmutable)1333 TEST_F(OptionsTest, CFOptionsComposeImmutable) {
1334   // Build a DBOptions from an Immutable/Mutable one and verify that
1335   // we get same constituent options.
1336   ConfigOptions config_options;
1337   Random rnd(301);
1338   ColumnFamilyOptions base_opts, new_opts;
1339   DBOptions dummy;  // Needed to create ImmutableCFOptions
1340   test::RandomInitCFOptions(&base_opts, dummy, &rnd);
1341   MutableCFOptions m_opts(base_opts);
1342   ImmutableCFOptions i_opts(base_opts);
1343   UpdateColumnFamilyOptions(i_opts, &new_opts);
1344   UpdateColumnFamilyOptions(m_opts, &new_opts);
1345   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_opts,
1346                                                   new_opts));
1347   delete new_opts.compaction_filter;
1348 }
1349 
TEST_F(OptionsTest,GetMutableCFOptions)1350 TEST_F(OptionsTest, GetMutableCFOptions) {
1351   Random rnd(228);
1352   ColumnFamilyOptions base, copy;
1353   std::string opts_str;
1354   std::unordered_map<std::string, std::string> opts_map;
1355   ConfigOptions config_options;
1356   DBOptions dummy;  // Needed to create ImmutableCFOptions
1357 
1358   test::RandomInitCFOptions(&base, dummy, &rnd);
1359   ColumnFamilyOptions result;
1360   MutableCFOptions m_opts(base), new_opts;
1361 
1362   ASSERT_OK(GetStringFromMutableCFOptions(config_options, m_opts, &opts_str));
1363   ASSERT_OK(StringToMap(opts_str, &opts_map));
1364   ASSERT_OK(GetMutableOptionsFromStrings(m_opts, opts_map, nullptr, &new_opts));
1365   UpdateColumnFamilyOptions(ImmutableCFOptions(base), &copy);
1366   UpdateColumnFamilyOptions(new_opts, &copy);
1367 
1368   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base, copy));
1369   delete copy.compaction_filter;
1370 }
1371 
TEST_F(OptionsTest,ColumnFamilyOptionsSerialization)1372 TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) {
1373   Options options;
1374   ColumnFamilyOptions base_opt, new_opt;
1375   Random rnd(302);
1376   ConfigOptions config_options;
1377   config_options.input_strings_escaped = false;
1378 
1379   // Phase 1: randomly assign base_opt
1380   // custom type options
1381   test::RandomInitCFOptions(&base_opt, options, &rnd);
1382 
1383   // Phase 2: obtain a string from base_opt
1384   std::string base_options_file_content;
1385   ASSERT_OK(GetStringFromColumnFamilyOptions(config_options, base_opt,
1386                                              &base_options_file_content));
1387 
1388   // Phase 3: Set new_opt from the derived string and expect
1389   //          new_opt == base_opt
1390   ASSERT_OK(
1391       GetColumnFamilyOptionsFromString(config_options, ColumnFamilyOptions(),
1392                                        base_options_file_content, &new_opt));
1393   ASSERT_OK(
1394       RocksDBOptionsParser::VerifyCFOptions(config_options, base_opt, new_opt));
1395   if (base_opt.compaction_filter) {
1396     delete base_opt.compaction_filter;
1397   }
1398 }
1399 
TEST_F(OptionsTest,CheckBlockBasedTableOptions)1400 TEST_F(OptionsTest, CheckBlockBasedTableOptions) {
1401   ColumnFamilyOptions cf_opts;
1402   DBOptions db_opts;
1403   ConfigOptions config_opts;
1404 
1405   ASSERT_OK(GetColumnFamilyOptionsFromString(
1406       config_opts, cf_opts, "prefix_extractor=capped:8", &cf_opts));
1407   ASSERT_OK(TableFactory::CreateFromString(config_opts, "BlockBasedTable",
1408                                            &cf_opts.table_factory));
1409   ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1410   ASSERT_TRUE(cf_opts.table_factory->IsInstanceOf(
1411       TableFactory::kBlockBasedTableName()));
1412   auto bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1413   ASSERT_OK(cf_opts.table_factory->ConfigureFromString(
1414       config_opts,
1415       "block_cache={capacity=1M;num_shard_bits=4;};"
1416       "block_size_deviation=101;"
1417       "block_restart_interval=0;"
1418       "index_block_restart_interval=5;"
1419       "partition_filters=true;"
1420       "index_type=kHashSearch;"
1421       "no_block_cache=1;"));
1422   ASSERT_NE(bbto, nullptr);
1423   ASSERT_EQ(bbto->block_cache.get(), nullptr);
1424   ASSERT_EQ(bbto->block_size_deviation, 0);
1425   ASSERT_EQ(bbto->block_restart_interval, 1);
1426   ASSERT_EQ(bbto->index_block_restart_interval, 1);
1427   ASSERT_FALSE(bbto->partition_filters);
1428   ASSERT_OK(TableFactory::CreateFromString(config_opts, "BlockBasedTable",
1429                                            &cf_opts.table_factory));
1430   bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1431 
1432   ASSERT_OK(cf_opts.table_factory->ConfigureFromString(config_opts,
1433                                                        "no_block_cache=0;"));
1434   ASSERT_NE(bbto->block_cache.get(), nullptr);
1435   ASSERT_OK(cf_opts.table_factory->ValidateOptions(db_opts, cf_opts));
1436 }
1437 
TEST_F(OptionsTest,MutableTableOptions)1438 TEST_F(OptionsTest, MutableTableOptions) {
1439   ConfigOptions config_options;
1440   std::shared_ptr<TableFactory> bbtf;
1441   bbtf.reset(NewBlockBasedTableFactory());
1442   auto bbto = bbtf->GetOptions<BlockBasedTableOptions>();
1443   ASSERT_NE(bbto, nullptr);
1444   ASSERT_FALSE(bbtf->IsPrepared());
1445   ASSERT_OK(bbtf->ConfigureOption(config_options, "block_align", "true"));
1446   ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "1024"));
1447   ASSERT_EQ(bbto->block_align, true);
1448   ASSERT_EQ(bbto->block_size, 1024);
1449   ASSERT_OK(bbtf->PrepareOptions(config_options));
1450   ASSERT_TRUE(bbtf->IsPrepared());
1451   config_options.mutable_options_only = true;
1452   ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "1024"));
1453   ASSERT_EQ(bbto->block_align, true);
1454   ASSERT_NOK(bbtf->ConfigureOption(config_options, "block_align", "false"));
1455   ASSERT_OK(bbtf->ConfigureOption(config_options, "block_size", "2048"));
1456   ASSERT_EQ(bbto->block_align, true);
1457   ASSERT_EQ(bbto->block_size, 2048);
1458 
1459   ColumnFamilyOptions cf_opts;
1460   cf_opts.table_factory = bbtf;
1461   ASSERT_NOK(GetColumnFamilyOptionsFromString(
1462       config_options, cf_opts, "block_based_table_factory.block_align=false",
1463       &cf_opts));
1464   ASSERT_OK(GetColumnFamilyOptionsFromString(
1465       config_options, cf_opts, "block_based_table_factory.block_size=8192",
1466       &cf_opts));
1467   ASSERT_EQ(bbto->block_align, true);
1468   ASSERT_EQ(bbto->block_size, 8192);
1469 }
1470 
TEST_F(OptionsTest,MutableCFOptions)1471 TEST_F(OptionsTest, MutableCFOptions) {
1472   ConfigOptions config_options;
1473   ColumnFamilyOptions cf_opts;
1474 
1475   ASSERT_OK(GetColumnFamilyOptionsFromString(
1476       config_options, cf_opts,
1477       "paranoid_file_checks=true; block_based_table_factory.block_align=false; "
1478       "block_based_table_factory.block_size=8192;",
1479       &cf_opts));
1480   ASSERT_TRUE(cf_opts.paranoid_file_checks);
1481   ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1482   const auto bbto = cf_opts.table_factory->GetOptions<BlockBasedTableOptions>();
1483   ASSERT_NE(bbto, nullptr);
1484   ASSERT_EQ(bbto->block_size, 8192);
1485   ASSERT_EQ(bbto->block_align, false);
1486   std::unordered_map<std::string, std::string> unused_opts;
1487   ASSERT_OK(GetColumnFamilyOptionsFromMap(
1488       config_options, cf_opts, {{"paranoid_file_checks", "false"}}, &cf_opts));
1489   ASSERT_EQ(cf_opts.paranoid_file_checks, false);
1490 
1491   ASSERT_OK(GetColumnFamilyOptionsFromMap(
1492       config_options, cf_opts,
1493       {{"block_based_table_factory.block_size", "16384"}}, &cf_opts));
1494   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1495   ASSERT_EQ(bbto->block_size, 16384);
1496 
1497   config_options.mutable_options_only = true;
1498   // Force consistency checks is not mutable
1499   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1500       config_options, cf_opts, {{"force_consistency_checks", "true"}},
1501       &cf_opts));
1502 
1503   // Attempt to change the table.  It is not mutable, so this should fail and
1504   // leave the original intact
1505   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1506       config_options, cf_opts, {{"table_factory", "PlainTable"}}, &cf_opts));
1507   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1508       config_options, cf_opts, {{"table_factory.id", "PlainTable"}}, &cf_opts));
1509   ASSERT_NE(cf_opts.table_factory.get(), nullptr);
1510   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1511 
1512   // Change the block size.  Should update the value in the current table
1513   ASSERT_OK(GetColumnFamilyOptionsFromMap(
1514       config_options, cf_opts,
1515       {{"block_based_table_factory.block_size", "8192"}}, &cf_opts));
1516   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1517   ASSERT_EQ(bbto->block_size, 8192);
1518 
1519   // Attempt to turn off block cache fails, as this option is not mutable
1520   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1521       config_options, cf_opts,
1522       {{"block_based_table_factory.no_block_cache", "true"}}, &cf_opts));
1523   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1524 
1525   // Attempt to change the block size via a config string/map.  Should update
1526   // the current value
1527   ASSERT_OK(GetColumnFamilyOptionsFromMap(
1528       config_options, cf_opts,
1529       {{"block_based_table_factory", "{block_size=32768}"}}, &cf_opts));
1530   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1531   ASSERT_EQ(bbto->block_size, 32768);
1532 
1533   // Attempt to change the block size and no cache through the map.  Should
1534   // fail, leaving the old values intact
1535   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
1536       config_options, cf_opts,
1537       {{"block_based_table_factory",
1538         "{block_size=16384; no_block_cache=true}"}},
1539       &cf_opts));
1540   ASSERT_EQ(bbto, cf_opts.table_factory->GetOptions<BlockBasedTableOptions>());
1541   ASSERT_EQ(bbto->block_size, 32768);
1542 }
1543 
1544 #endif  // !ROCKSDB_LITE
1545 
1546 Status StringToMap(
1547     const std::string& opts_str,
1548     std::unordered_map<std::string, std::string>* opts_map);
1549 
1550 #ifndef ROCKSDB_LITE  // StringToMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,StringToMapTest)1551 TEST_F(OptionsTest, StringToMapTest) {
1552   std::unordered_map<std::string, std::string> opts_map;
1553   // Regular options
1554   ASSERT_OK(StringToMap("k1=v1;k2=v2;k3=v3", &opts_map));
1555   ASSERT_EQ(opts_map["k1"], "v1");
1556   ASSERT_EQ(opts_map["k2"], "v2");
1557   ASSERT_EQ(opts_map["k3"], "v3");
1558   // Value with '='
1559   opts_map.clear();
1560   ASSERT_OK(StringToMap("k1==v1;k2=v2=;", &opts_map));
1561   ASSERT_EQ(opts_map["k1"], "=v1");
1562   ASSERT_EQ(opts_map["k2"], "v2=");
1563   // Overwrriten option
1564   opts_map.clear();
1565   ASSERT_OK(StringToMap("k1=v1;k1=v2;k3=v3", &opts_map));
1566   ASSERT_EQ(opts_map["k1"], "v2");
1567   ASSERT_EQ(opts_map["k3"], "v3");
1568   // Empty value
1569   opts_map.clear();
1570   ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=", &opts_map));
1571   ASSERT_EQ(opts_map["k1"], "v1");
1572   ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1573   ASSERT_EQ(opts_map["k2"], "");
1574   ASSERT_EQ(opts_map["k3"], "v3");
1575   ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
1576   ASSERT_EQ(opts_map["k4"], "");
1577   opts_map.clear();
1578   ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=   ", &opts_map));
1579   ASSERT_EQ(opts_map["k1"], "v1");
1580   ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1581   ASSERT_EQ(opts_map["k2"], "");
1582   ASSERT_EQ(opts_map["k3"], "v3");
1583   ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
1584   ASSERT_EQ(opts_map["k4"], "");
1585   opts_map.clear();
1586   ASSERT_OK(StringToMap("k1=v1;k2=;k3=", &opts_map));
1587   ASSERT_EQ(opts_map["k1"], "v1");
1588   ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1589   ASSERT_EQ(opts_map["k2"], "");
1590   ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
1591   ASSERT_EQ(opts_map["k3"], "");
1592   opts_map.clear();
1593   ASSERT_OK(StringToMap("k1=v1;k2=;k3=;", &opts_map));
1594   ASSERT_EQ(opts_map["k1"], "v1");
1595   ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
1596   ASSERT_EQ(opts_map["k2"], "");
1597   ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
1598   ASSERT_EQ(opts_map["k3"], "");
1599   // Regular nested options
1600   opts_map.clear();
1601   ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2=nv2};k3=v3", &opts_map));
1602   ASSERT_EQ(opts_map["k1"], "v1");
1603   ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2=nv2");
1604   ASSERT_EQ(opts_map["k3"], "v3");
1605   // Multi-level nested options
1606   opts_map.clear();
1607   ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2={nnk1=nnk2}};"
1608                         "k3={nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}};k4=v4",
1609                         &opts_map));
1610   ASSERT_EQ(opts_map["k1"], "v1");
1611   ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2={nnk1=nnk2}");
1612   ASSERT_EQ(opts_map["k3"], "nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}");
1613   ASSERT_EQ(opts_map["k4"], "v4");
1614   // Garbage inside curly braces
1615   opts_map.clear();
1616   ASSERT_OK(StringToMap("k1=v1;k2={dfad=};k3={=};k4=v4",
1617                         &opts_map));
1618   ASSERT_EQ(opts_map["k1"], "v1");
1619   ASSERT_EQ(opts_map["k2"], "dfad=");
1620   ASSERT_EQ(opts_map["k3"], "=");
1621   ASSERT_EQ(opts_map["k4"], "v4");
1622   // Empty nested options
1623   opts_map.clear();
1624   ASSERT_OK(StringToMap("k1=v1;k2={};", &opts_map));
1625   ASSERT_EQ(opts_map["k1"], "v1");
1626   ASSERT_EQ(opts_map["k2"], "");
1627   opts_map.clear();
1628   ASSERT_OK(StringToMap("k1=v1;k2={{{{}}}{}{}};", &opts_map));
1629   ASSERT_EQ(opts_map["k1"], "v1");
1630   ASSERT_EQ(opts_map["k2"], "{{{}}}{}{}");
1631   // With random spaces
1632   opts_map.clear();
1633   ASSERT_OK(StringToMap("  k1 =  v1 ; k2= {nk1=nv1; nk2={nnk1=nnk2}}  ; "
1634                         "k3={  {   } }; k4= v4  ",
1635                         &opts_map));
1636   ASSERT_EQ(opts_map["k1"], "v1");
1637   ASSERT_EQ(opts_map["k2"], "nk1=nv1; nk2={nnk1=nnk2}");
1638   ASSERT_EQ(opts_map["k3"], "{   }");
1639   ASSERT_EQ(opts_map["k4"], "v4");
1640 
1641   // Empty key
1642   ASSERT_NOK(StringToMap("k1=v1;k2=v2;=", &opts_map));
1643   ASSERT_NOK(StringToMap("=v1;k2=v2", &opts_map));
1644   ASSERT_NOK(StringToMap("k1=v1;k2v2;", &opts_map));
1645   ASSERT_NOK(StringToMap("k1=v1;k2=v2;fadfa", &opts_map));
1646   ASSERT_NOK(StringToMap("k1=v1;k2=v2;;", &opts_map));
1647   // Mismatch curly braces
1648   ASSERT_NOK(StringToMap("k1=v1;k2={;k3=v3", &opts_map));
1649   ASSERT_NOK(StringToMap("k1=v1;k2={{};k3=v3", &opts_map));
1650   ASSERT_NOK(StringToMap("k1=v1;k2={}};k3=v3", &opts_map));
1651   ASSERT_NOK(StringToMap("k1=v1;k2={{}{}}};k3=v3", &opts_map));
1652   // However this is valid!
1653   opts_map.clear();
1654   ASSERT_OK(StringToMap("k1=v1;k2=};k3=v3", &opts_map));
1655   ASSERT_EQ(opts_map["k1"], "v1");
1656   ASSERT_EQ(opts_map["k2"], "}");
1657   ASSERT_EQ(opts_map["k3"], "v3");
1658 
1659   // Invalid chars after closing curly brace
1660   ASSERT_NOK(StringToMap("k1=v1;k2={{}}{};k3=v3", &opts_map));
1661   ASSERT_NOK(StringToMap("k1=v1;k2={{}}cfda;k3=v3", &opts_map));
1662   ASSERT_NOK(StringToMap("k1=v1;k2={{}}  cfda;k3=v3", &opts_map));
1663   ASSERT_NOK(StringToMap("k1=v1;k2={{}}  cfda", &opts_map));
1664   ASSERT_NOK(StringToMap("k1=v1;k2={{}}{}", &opts_map));
1665   ASSERT_NOK(StringToMap("k1=v1;k2={{dfdl}adfa}{}", &opts_map));
1666 }
1667 #endif  // ROCKSDB_LITE
1668 
1669 #ifndef ROCKSDB_LITE  // StringToMap is not supported in ROCKSDB_LITE
TEST_F(OptionsTest,StringToMapRandomTest)1670 TEST_F(OptionsTest, StringToMapRandomTest) {
1671   std::unordered_map<std::string, std::string> opts_map;
1672   // Make sure segfault is not hit by semi-random strings
1673 
1674   std::vector<std::string> bases = {
1675       "a={aa={};tt={xxx={}}};c=defff",
1676       "a={aa={};tt={xxx={}}};c=defff;d={{}yxx{}3{xx}}",
1677       "abc={{}{}{}{{{}}}{{}{}{}{}{}{}{}"};
1678 
1679   for (std::string base : bases) {
1680     for (int rand_seed = 301; rand_seed < 401; rand_seed++) {
1681       Random rnd(rand_seed);
1682       for (int attempt = 0; attempt < 10; attempt++) {
1683         std::string str = base;
1684         // Replace random position to space
1685         size_t pos = static_cast<size_t>(
1686             rnd.Uniform(static_cast<int>(base.size())));
1687         str[pos] = ' ';
1688         Status s = StringToMap(str, &opts_map);
1689         ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1690         opts_map.clear();
1691       }
1692     }
1693   }
1694 
1695   // Random Construct a string
1696   std::vector<char> chars = {'{', '}', ' ', '=', ';', 'c'};
1697   for (int rand_seed = 301; rand_seed < 1301; rand_seed++) {
1698     Random rnd(rand_seed);
1699     int len = rnd.Uniform(30);
1700     std::string str = "";
1701     for (int attempt = 0; attempt < len; attempt++) {
1702       // Add a random character
1703       size_t pos = static_cast<size_t>(
1704           rnd.Uniform(static_cast<int>(chars.size())));
1705       str.append(1, chars[pos]);
1706     }
1707     Status s = StringToMap(str, &opts_map);
1708     ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1709     s = StringToMap("name=" + str, &opts_map);
1710     ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
1711     opts_map.clear();
1712   }
1713 }
1714 
TEST_F(OptionsTest,GetStringFromCompressionType)1715 TEST_F(OptionsTest, GetStringFromCompressionType) {
1716   std::string res;
1717 
1718   ASSERT_OK(GetStringFromCompressionType(&res, kNoCompression));
1719   ASSERT_EQ(res, "kNoCompression");
1720 
1721   ASSERT_OK(GetStringFromCompressionType(&res, kSnappyCompression));
1722   ASSERT_EQ(res, "kSnappyCompression");
1723 
1724   ASSERT_OK(GetStringFromCompressionType(&res, kDisableCompressionOption));
1725   ASSERT_EQ(res, "kDisableCompressionOption");
1726 
1727   ASSERT_OK(GetStringFromCompressionType(&res, kLZ4Compression));
1728   ASSERT_EQ(res, "kLZ4Compression");
1729 
1730   ASSERT_OK(GetStringFromCompressionType(&res, kZlibCompression));
1731   ASSERT_EQ(res, "kZlibCompression");
1732 
1733   ASSERT_NOK(
1734       GetStringFromCompressionType(&res, static_cast<CompressionType>(-10)));
1735 }
1736 
TEST_F(OptionsTest,OnlyMutableDBOptions)1737 TEST_F(OptionsTest, OnlyMutableDBOptions) {
1738   std::string opt_str;
1739   Random rnd(302);
1740   ConfigOptions cfg_opts;
1741   DBOptions db_opts;
1742   DBOptions mdb_opts;
1743   std::unordered_set<std::string> m_names;
1744   std::unordered_set<std::string> a_names;
1745 
1746   test::RandomInitDBOptions(&db_opts, &rnd);
1747   auto db_config = DBOptionsAsConfigurable(db_opts);
1748 
1749   // Get all of the DB Option names (mutable or not)
1750   ASSERT_OK(db_config->GetOptionNames(cfg_opts, &a_names));
1751 
1752   // Get only the mutable options from db_opts and set those in mdb_opts
1753   cfg_opts.mutable_options_only = true;
1754 
1755   // Get only the Mutable DB Option names
1756   ASSERT_OK(db_config->GetOptionNames(cfg_opts, &m_names));
1757   ASSERT_OK(GetStringFromDBOptions(cfg_opts, db_opts, &opt_str));
1758   ASSERT_OK(GetDBOptionsFromString(cfg_opts, mdb_opts, opt_str, &mdb_opts));
1759   std::string mismatch;
1760   // Comparing only the mutable options, the two are equivalent
1761   auto mdb_config = DBOptionsAsConfigurable(mdb_opts);
1762   ASSERT_TRUE(mdb_config->AreEquivalent(cfg_opts, db_config.get(), &mismatch));
1763   ASSERT_TRUE(db_config->AreEquivalent(cfg_opts, mdb_config.get(), &mismatch));
1764 
1765   ASSERT_GT(a_names.size(), m_names.size());
1766   for (const auto& n : m_names) {
1767     std::string m, d;
1768     ASSERT_OK(mdb_config->GetOption(cfg_opts, n, &m));
1769     ASSERT_OK(db_config->GetOption(cfg_opts, n, &d));
1770     ASSERT_EQ(m, d);
1771   }
1772 
1773   cfg_opts.mutable_options_only = false;
1774   // Comparing all of the options, the two are not equivalent
1775   ASSERT_FALSE(mdb_config->AreEquivalent(cfg_opts, db_config.get(), &mismatch));
1776   ASSERT_FALSE(db_config->AreEquivalent(cfg_opts, mdb_config.get(), &mismatch));
1777 }
1778 
TEST_F(OptionsTest,OnlyMutableCFOptions)1779 TEST_F(OptionsTest, OnlyMutableCFOptions) {
1780   std::string opt_str;
1781   Random rnd(302);
1782   ConfigOptions cfg_opts;
1783   DBOptions db_opts;
1784   ColumnFamilyOptions mcf_opts;
1785   ColumnFamilyOptions cf_opts;
1786   std::unordered_set<std::string> m_names;
1787   std::unordered_set<std::string> a_names;
1788 
1789   test::RandomInitCFOptions(&cf_opts, db_opts, &rnd);
1790   auto cf_config = CFOptionsAsConfigurable(cf_opts);
1791 
1792   // Get all of the CF Option names (mutable or not)
1793   ASSERT_OK(cf_config->GetOptionNames(cfg_opts, &a_names));
1794 
1795   // Get only the mutable options from cf_opts and set those in mcf_opts
1796   cfg_opts.mutable_options_only = true;
1797   // Get only the Mutable CF Option names
1798   ASSERT_OK(cf_config->GetOptionNames(cfg_opts, &m_names));
1799   ASSERT_OK(GetStringFromColumnFamilyOptions(cfg_opts, cf_opts, &opt_str));
1800   ASSERT_OK(
1801       GetColumnFamilyOptionsFromString(cfg_opts, mcf_opts, opt_str, &mcf_opts));
1802   std::string mismatch;
1803 
1804   auto mcf_config = CFOptionsAsConfigurable(mcf_opts);
1805   // Comparing only the mutable options, the two are equivalent
1806   ASSERT_TRUE(mcf_config->AreEquivalent(cfg_opts, cf_config.get(), &mismatch));
1807   ASSERT_TRUE(cf_config->AreEquivalent(cfg_opts, mcf_config.get(), &mismatch));
1808 
1809   ASSERT_GT(a_names.size(), m_names.size());
1810   for (const auto& n : m_names) {
1811     std::string m, d;
1812     ASSERT_OK(mcf_config->GetOption(cfg_opts, n, &m));
1813     ASSERT_OK(cf_config->GetOption(cfg_opts, n, &d));
1814     ASSERT_EQ(m, d);
1815   }
1816 
1817   cfg_opts.mutable_options_only = false;
1818   // Comparing all of the options, the two are not equivalent
1819   ASSERT_FALSE(mcf_config->AreEquivalent(cfg_opts, cf_config.get(), &mismatch));
1820   ASSERT_FALSE(cf_config->AreEquivalent(cfg_opts, mcf_config.get(), &mismatch));
1821 
1822   delete cf_opts.compaction_filter;
1823 }
1824 #endif  // !ROCKSDB_LITE
1825 
TEST_F(OptionsTest,ConvertOptionsTest)1826 TEST_F(OptionsTest, ConvertOptionsTest) {
1827   LevelDBOptions leveldb_opt;
1828   Options converted_opt = ConvertOptions(leveldb_opt);
1829 
1830   ASSERT_EQ(converted_opt.create_if_missing, leveldb_opt.create_if_missing);
1831   ASSERT_EQ(converted_opt.error_if_exists, leveldb_opt.error_if_exists);
1832   ASSERT_EQ(converted_opt.paranoid_checks, leveldb_opt.paranoid_checks);
1833   ASSERT_EQ(converted_opt.env, leveldb_opt.env);
1834   ASSERT_EQ(converted_opt.info_log.get(), leveldb_opt.info_log);
1835   ASSERT_EQ(converted_opt.write_buffer_size, leveldb_opt.write_buffer_size);
1836   ASSERT_EQ(converted_opt.max_open_files, leveldb_opt.max_open_files);
1837   ASSERT_EQ(converted_opt.compression, leveldb_opt.compression);
1838 
1839   std::shared_ptr<TableFactory> table_factory = converted_opt.table_factory;
1840   const auto table_opt = table_factory->GetOptions<BlockBasedTableOptions>();
1841   ASSERT_NE(table_opt, nullptr);
1842 
1843   ASSERT_EQ(table_opt->block_cache->GetCapacity(), 8UL << 20);
1844   ASSERT_EQ(table_opt->block_size, leveldb_opt.block_size);
1845   ASSERT_EQ(table_opt->block_restart_interval,
1846             leveldb_opt.block_restart_interval);
1847   ASSERT_EQ(table_opt->filter_policy.get(), leveldb_opt.filter_policy);
1848 }
1849 
1850 #ifndef ROCKSDB_LITE
1851 const static std::string kCustomEnvName = "Custom";
1852 const static std::string kCustomEnvProp = "env=" + kCustomEnvName;
1853 class CustomEnv : public EnvWrapper {
1854  public:
CustomEnv(Env * _target)1855   explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
1856 };
1857 
RegisterCustomEnv(ObjectLibrary & library,const std::string & arg)1858 static int RegisterCustomEnv(ObjectLibrary& library, const std::string& arg) {
1859   library.Register<Env>(
1860       arg, [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
1861               std::string* /* errmsg */) {
1862         static CustomEnv env(Env::Default());
1863         return &env;
1864       });
1865   return 1;
1866 }
1867 
1868 // This test suite tests the old APIs into the Configure options methods.
1869 // Once those APIs are officially deprecated, this test suite can be deleted.
1870 class OptionsOldApiTest : public testing::Test {};
1871 
TEST_F(OptionsOldApiTest,GetOptionsFromMapTest)1872 TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
1873   std::unordered_map<std::string, std::string> cf_options_map = {
1874       {"write_buffer_size", "1"},
1875       {"max_write_buffer_number", "2"},
1876       {"min_write_buffer_number_to_merge", "3"},
1877       {"max_write_buffer_number_to_maintain", "99"},
1878       {"max_write_buffer_size_to_maintain", "-99999"},
1879       {"compression", "kSnappyCompression"},
1880       {"compression_per_level",
1881        "kNoCompression:"
1882        "kSnappyCompression:"
1883        "kZlibCompression:"
1884        "kBZip2Compression:"
1885        "kLZ4Compression:"
1886        "kLZ4HCCompression:"
1887        "kXpressCompression:"
1888        "kZSTD:"
1889        "kZSTDNotFinalCompression"},
1890       {"bottommost_compression", "kLZ4Compression"},
1891       {"bottommost_compression_opts", "5:6:7:8:9:true"},
1892       {"compression_opts", "4:5:6:7:8:true"},
1893       {"num_levels", "8"},
1894       {"level0_file_num_compaction_trigger", "8"},
1895       {"level0_slowdown_writes_trigger", "9"},
1896       {"level0_stop_writes_trigger", "10"},
1897       {"target_file_size_base", "12"},
1898       {"target_file_size_multiplier", "13"},
1899       {"max_bytes_for_level_base", "14"},
1900       {"level_compaction_dynamic_level_bytes", "true"},
1901       {"max_bytes_for_level_multiplier", "15.0"},
1902       {"max_bytes_for_level_multiplier_additional", "16:17:18"},
1903       {"max_compaction_bytes", "21"},
1904       {"soft_rate_limit", "1.1"},
1905       {"hard_rate_limit", "2.1"},
1906       {"hard_pending_compaction_bytes_limit", "211"},
1907       {"arena_block_size", "22"},
1908       {"disable_auto_compactions", "true"},
1909       {"compaction_style", "kCompactionStyleLevel"},
1910       {"compaction_pri", "kOldestSmallestSeqFirst"},
1911       {"verify_checksums_in_compaction", "false"},
1912       {"compaction_options_fifo", "23"},
1913       {"max_sequential_skip_in_iterations", "24"},
1914       {"inplace_update_support", "true"},
1915       {"report_bg_io_stats", "true"},
1916       {"compaction_measure_io_stats", "false"},
1917       {"inplace_update_num_locks", "25"},
1918       {"memtable_prefix_bloom_size_ratio", "0.26"},
1919       {"memtable_whole_key_filtering", "true"},
1920       {"memtable_huge_page_size", "28"},
1921       {"bloom_locality", "29"},
1922       {"max_successive_merges", "30"},
1923       {"min_partial_merge_operands", "31"},
1924       {"prefix_extractor", "fixed:31"},
1925       {"optimize_filters_for_hits", "true"},
1926       {"enable_blob_files", "true"},
1927       {"min_blob_size", "1K"},
1928       {"blob_file_size", "1G"},
1929       {"blob_compression_type", "kZSTD"},
1930       {"enable_blob_garbage_collection", "true"},
1931       {"blob_garbage_collection_age_cutoff", "0.5"},
1932   };
1933 
1934   std::unordered_map<std::string, std::string> db_options_map = {
1935       {"create_if_missing", "false"},
1936       {"create_missing_column_families", "true"},
1937       {"error_if_exists", "false"},
1938       {"paranoid_checks", "true"},
1939       {"track_and_verify_wals_in_manifest", "true"},
1940       {"max_open_files", "32"},
1941       {"max_total_wal_size", "33"},
1942       {"use_fsync", "true"},
1943       {"db_log_dir", "/db_log_dir"},
1944       {"wal_dir", "/wal_dir"},
1945       {"delete_obsolete_files_period_micros", "34"},
1946       {"max_background_compactions", "35"},
1947       {"max_background_flushes", "36"},
1948       {"max_log_file_size", "37"},
1949       {"log_file_time_to_roll", "38"},
1950       {"keep_log_file_num", "39"},
1951       {"recycle_log_file_num", "5"},
1952       {"max_manifest_file_size", "40"},
1953       {"table_cache_numshardbits", "41"},
1954       {"WAL_ttl_seconds", "43"},
1955       {"WAL_size_limit_MB", "44"},
1956       {"manifest_preallocation_size", "45"},
1957       {"allow_mmap_reads", "true"},
1958       {"allow_mmap_writes", "false"},
1959       {"use_direct_reads", "false"},
1960       {"use_direct_io_for_flush_and_compaction", "false"},
1961       {"is_fd_close_on_exec", "true"},
1962       {"skip_log_error_on_recovery", "false"},
1963       {"stats_dump_period_sec", "46"},
1964       {"stats_persist_period_sec", "57"},
1965       {"persist_stats_to_disk", "false"},
1966       {"stats_history_buffer_size", "69"},
1967       {"advise_random_on_open", "true"},
1968       {"use_adaptive_mutex", "false"},
1969       {"new_table_reader_for_compaction_inputs", "true"},
1970       {"compaction_readahead_size", "100"},
1971       {"random_access_max_buffer_size", "3145728"},
1972       {"writable_file_max_buffer_size", "314159"},
1973       {"bytes_per_sync", "47"},
1974       {"wal_bytes_per_sync", "48"},
1975       {"strict_bytes_per_sync", "true"},
1976   };
1977 
1978   ColumnFamilyOptions base_cf_opt;
1979   ColumnFamilyOptions new_cf_opt;
1980   ASSERT_OK(GetColumnFamilyOptionsFromMap(
1981             base_cf_opt, cf_options_map, &new_cf_opt));
1982   ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
1983   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
1984   ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
1985   ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
1986   ASSERT_EQ(new_cf_opt.max_write_buffer_size_to_maintain, -99999);
1987   ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
1988   ASSERT_EQ(new_cf_opt.compression_per_level.size(), 9U);
1989   ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
1990   ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
1991   ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
1992   ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
1993   ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
1994   ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
1995   ASSERT_EQ(new_cf_opt.compression_per_level[6], kXpressCompression);
1996   ASSERT_EQ(new_cf_opt.compression_per_level[7], kZSTD);
1997   ASSERT_EQ(new_cf_opt.compression_per_level[8], kZSTDNotFinalCompression);
1998   ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
1999   ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
2000   ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
2001   ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7u);
2002   ASSERT_EQ(new_cf_opt.compression_opts.zstd_max_train_bytes, 8u);
2003   ASSERT_EQ(new_cf_opt.compression_opts.parallel_threads,
2004             CompressionOptions().parallel_threads);
2005   ASSERT_EQ(new_cf_opt.compression_opts.enabled, true);
2006   ASSERT_EQ(new_cf_opt.bottommost_compression, kLZ4Compression);
2007   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.window_bits, 5);
2008   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.level, 6);
2009   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.strategy, 7);
2010   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.max_dict_bytes, 8u);
2011   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.zstd_max_train_bytes, 9u);
2012   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.parallel_threads,
2013             CompressionOptions().parallel_threads);
2014   ASSERT_EQ(new_cf_opt.bottommost_compression_opts.enabled, true);
2015   ASSERT_EQ(new_cf_opt.num_levels, 8);
2016   ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
2017   ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
2018   ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
2019   ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
2020   ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
2021   ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
2022   ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
2023   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15.0);
2024   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
2025   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
2026   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
2027   ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
2028   ASSERT_EQ(new_cf_opt.max_compaction_bytes, 21);
2029   ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
2030   ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
2031   ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
2032   ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
2033   ASSERT_EQ(new_cf_opt.compaction_pri, kOldestSmallestSeqFirst);
2034   ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
2035             static_cast<uint64_t>(23));
2036   ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
2037             static_cast<uint64_t>(24));
2038   ASSERT_EQ(new_cf_opt.inplace_update_support, true);
2039   ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
2040   ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_size_ratio, 0.26);
2041   ASSERT_EQ(new_cf_opt.memtable_whole_key_filtering, true);
2042   ASSERT_EQ(new_cf_opt.memtable_huge_page_size, 28U);
2043   ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
2044   ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
2045   ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
2046   ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
2047   ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
2048             "rocksdb.FixedPrefix.31");
2049   ASSERT_EQ(new_cf_opt.enable_blob_files, true);
2050   ASSERT_EQ(new_cf_opt.min_blob_size, 1ULL << 10);
2051   ASSERT_EQ(new_cf_opt.blob_file_size, 1ULL << 30);
2052   ASSERT_EQ(new_cf_opt.blob_compression_type, kZSTD);
2053   ASSERT_EQ(new_cf_opt.enable_blob_garbage_collection, true);
2054   ASSERT_EQ(new_cf_opt.blob_garbage_collection_age_cutoff, 0.5);
2055 
2056   cf_options_map["write_buffer_size"] = "hello";
2057   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
2058              base_cf_opt, cf_options_map, &new_cf_opt));
2059   ConfigOptions exact, loose;
2060   exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2061   loose.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
2062 
2063   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2064 
2065   cf_options_map["write_buffer_size"] = "1";
2066   ASSERT_OK(GetColumnFamilyOptionsFromMap(
2067             base_cf_opt, cf_options_map, &new_cf_opt));
2068 
2069   cf_options_map["unknown_option"] = "1";
2070   ASSERT_NOK(GetColumnFamilyOptionsFromMap(
2071              base_cf_opt, cf_options_map, &new_cf_opt));
2072   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2073 
2074   ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
2075                                           &new_cf_opt,
2076                                           false, /* input_strings_escaped  */
2077                                           true /* ignore_unknown_options */));
2078   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
2079       loose, base_cf_opt, new_cf_opt, nullptr /* new_opt_map */));
2080   ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
2081       exact /* default for VerifyCFOptions */, base_cf_opt, new_cf_opt, nullptr));
2082 
2083   DBOptions base_db_opt;
2084   DBOptions new_db_opt;
2085   ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2086   ASSERT_EQ(new_db_opt.create_if_missing, false);
2087   ASSERT_EQ(new_db_opt.create_missing_column_families, true);
2088   ASSERT_EQ(new_db_opt.error_if_exists, false);
2089   ASSERT_EQ(new_db_opt.paranoid_checks, true);
2090   ASSERT_EQ(new_db_opt.track_and_verify_wals_in_manifest, true);
2091   ASSERT_EQ(new_db_opt.max_open_files, 32);
2092   ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
2093   ASSERT_EQ(new_db_opt.use_fsync, true);
2094   ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
2095   ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
2096   ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
2097             static_cast<uint64_t>(34));
2098   ASSERT_EQ(new_db_opt.max_background_compactions, 35);
2099   ASSERT_EQ(new_db_opt.max_background_flushes, 36);
2100   ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
2101   ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
2102   ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
2103   ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
2104   ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
2105   ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
2106   ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
2107   ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
2108   ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
2109   ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
2110   ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
2111   ASSERT_EQ(new_db_opt.use_direct_reads, false);
2112   ASSERT_EQ(new_db_opt.use_direct_io_for_flush_and_compaction, false);
2113   ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
2114   ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
2115   ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
2116   ASSERT_EQ(new_db_opt.stats_persist_period_sec, 57U);
2117   ASSERT_EQ(new_db_opt.persist_stats_to_disk, false);
2118   ASSERT_EQ(new_db_opt.stats_history_buffer_size, 69U);
2119   ASSERT_EQ(new_db_opt.advise_random_on_open, true);
2120   ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
2121   ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
2122   ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
2123   ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
2124   ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
2125   ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
2126   ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
2127   ASSERT_EQ(new_db_opt.strict_bytes_per_sync, true);
2128 
2129   db_options_map["max_open_files"] = "hello";
2130   ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2131   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2132   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
2133 
2134   // unknow options should fail parsing without ignore_unknown_options = true
2135   db_options_map["unknown_db_option"] = "1";
2136   ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
2137   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2138 
2139   ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt,
2140                                 false, /* input_strings_escaped  */
2141                                 true /* ignore_unknown_options */));
2142   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
2143   ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
2144 }
2145 
TEST_F(OptionsOldApiTest,GetColumnFamilyOptionsFromStringTest)2146 TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
2147   ColumnFamilyOptions base_cf_opt;
2148   ColumnFamilyOptions new_cf_opt;
2149   base_cf_opt.table_factory.reset();
2150   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt, "", &new_cf_opt));
2151   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2152             "write_buffer_size=5", &new_cf_opt));
2153   ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
2154   ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
2155   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2156             "write_buffer_size=6;", &new_cf_opt));
2157   ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
2158   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2159             "  write_buffer_size =  7  ", &new_cf_opt));
2160   ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
2161   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2162             "  write_buffer_size =  8 ; ", &new_cf_opt));
2163   ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
2164   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2165             "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
2166   ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
2167   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
2168   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2169             "write_buffer_size=11; max_write_buffer_number  =  12 ;",
2170             &new_cf_opt));
2171   ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
2172   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
2173   // Wrong name "max_write_buffer_number_"
2174   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2175              "write_buffer_size=13;max_write_buffer_number_=14;",
2176               &new_cf_opt));
2177   ConfigOptions exact;
2178   exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2179   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2180 
2181   // Comparator from object registry
2182   std::string kCompName = "reverse_comp";
2183   ObjectLibrary::Default()->Register<const Comparator>(
2184       kCompName,
2185       [](const std::string& /*name*/,
2186          std::unique_ptr<const Comparator>* /*guard*/,
2187          std::string* /* errmsg */) { return ReverseBytewiseComparator(); });
2188 
2189   ASSERT_OK(GetColumnFamilyOptionsFromString(
2190       base_cf_opt, "comparator=" + kCompName + ";", &new_cf_opt));
2191   ASSERT_EQ(new_cf_opt.comparator, ReverseBytewiseComparator());
2192 
2193   // MergeOperator from object registry
2194   std::unique_ptr<BytesXOROperator> bxo(new BytesXOROperator());
2195   std::string kMoName = bxo->Name();
2196   ObjectLibrary::Default()->Register<MergeOperator>(
2197       kMoName,
2198       [](const std::string& /*name*/, std::unique_ptr<MergeOperator>* guard,
2199          std::string* /* errmsg */) {
2200         guard->reset(new BytesXOROperator());
2201         return guard->get();
2202       });
2203 
2204   ASSERT_OK(GetColumnFamilyOptionsFromString(
2205       base_cf_opt, "merge_operator=" + kMoName + ";", &new_cf_opt));
2206   ASSERT_EQ(kMoName, std::string(new_cf_opt.merge_operator->Name()));
2207 
2208   // Wrong key/value pair
2209   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2210              "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
2211   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2212 
2213   // Error Paring value
2214   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2215              "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
2216   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2217 
2218   // Missing option name
2219   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2220              "write_buffer_size=13; =100;", &new_cf_opt));
2221   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2222 
2223   const uint64_t kilo = 1024UL;
2224   const uint64_t mega = 1024 * kilo;
2225   const uint64_t giga = 1024 * mega;
2226   const uint64_t tera = 1024 * giga;
2227 
2228   // Units (k)
2229   ASSERT_OK(GetColumnFamilyOptionsFromString(
2230       base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
2231   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
2232   // Units (m)
2233   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2234             "max_write_buffer_number=16m;inplace_update_num_locks=17M",
2235             &new_cf_opt));
2236   ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
2237   ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17u * mega);
2238   // Units (g)
2239   ASSERT_OK(GetColumnFamilyOptionsFromString(
2240       base_cf_opt,
2241       "write_buffer_size=18g;prefix_extractor=capped:8;"
2242       "arena_block_size=19G",
2243       &new_cf_opt));
2244 
2245   ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
2246   ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
2247   ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
2248   std::string prefix_name(new_cf_opt.prefix_extractor->Name());
2249   ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
2250 
2251   // Units (t)
2252   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2253             "write_buffer_size=20t;arena_block_size=21T", &new_cf_opt));
2254   ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
2255   ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
2256 
2257   // Nested block based table options
2258   // Empty
2259   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2260             "write_buffer_size=10;max_write_buffer_number=16;"
2261             "block_based_table_factory={};arena_block_size=1024",
2262             &new_cf_opt));
2263   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2264   // Non-empty
2265   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2266             "write_buffer_size=10;max_write_buffer_number=16;"
2267             "block_based_table_factory={block_cache=1M;block_size=4;};"
2268             "arena_block_size=1024",
2269             &new_cf_opt));
2270   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2271   // Last one
2272   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2273             "write_buffer_size=10;max_write_buffer_number=16;"
2274             "block_based_table_factory={block_cache=1M;block_size=4;}",
2275             &new_cf_opt));
2276   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2277   // Mismatch curly braces
2278   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2279              "write_buffer_size=10;max_write_buffer_number=16;"
2280              "block_based_table_factory={{{block_size=4;};"
2281              "arena_block_size=1024",
2282              &new_cf_opt));
2283   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2284 
2285   // Unexpected chars after closing curly brace
2286   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2287              "write_buffer_size=10;max_write_buffer_number=16;"
2288              "block_based_table_factory={block_size=4;}};"
2289              "arena_block_size=1024",
2290              &new_cf_opt));
2291   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2292 
2293   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2294              "write_buffer_size=10;max_write_buffer_number=16;"
2295              "block_based_table_factory={block_size=4;}xdfa;"
2296              "arena_block_size=1024",
2297              &new_cf_opt));
2298   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2299 
2300   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2301              "write_buffer_size=10;max_write_buffer_number=16;"
2302              "block_based_table_factory={block_size=4;}xdfa",
2303              &new_cf_opt));
2304   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2305 
2306   // Invalid block based table option
2307   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2308              "write_buffer_size=10;max_write_buffer_number=16;"
2309              "block_based_table_factory={xx_block_size=4;}",
2310              &new_cf_opt));
2311   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2312 
2313   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2314            "optimize_filters_for_hits=true",
2315            &new_cf_opt));
2316   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2317             "optimize_filters_for_hits=false",
2318             &new_cf_opt));
2319 
2320   ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
2321               "optimize_filters_for_hits=junk",
2322               &new_cf_opt));
2323   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
2324 
2325   // Nested plain table options
2326   // Empty
2327   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2328             "write_buffer_size=10;max_write_buffer_number=16;"
2329             "plain_table_factory={};arena_block_size=1024",
2330             &new_cf_opt));
2331   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2332   ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
2333   // Non-empty
2334   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2335             "write_buffer_size=10;max_write_buffer_number=16;"
2336             "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
2337             "arena_block_size=1024",
2338             &new_cf_opt));
2339   ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
2340   ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
2341 
2342   // memtable factory
2343   ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
2344             "write_buffer_size=10;max_write_buffer_number=16;"
2345             "memtable=skip_list:10;arena_block_size=1024",
2346             &new_cf_opt));
2347   ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
2348   ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
2349 }
2350 
TEST_F(OptionsOldApiTest,GetBlockBasedTableOptionsFromString)2351 TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
2352   BlockBasedTableOptions table_opt;
2353   BlockBasedTableOptions new_opt;
2354   // make sure default values are overwritten by something else
2355   ASSERT_OK(GetBlockBasedTableOptionsFromString(
2356       table_opt,
2357       "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
2358       "checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
2359       "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
2360       "block_size_deviation=8;block_restart_interval=4;"
2361       "format_version=5;whole_key_filtering=1;"
2362       "filter_policy=bloomfilter:4.567:false;",
2363       &new_opt));
2364   ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
2365   ASSERT_EQ(new_opt.index_type, BlockBasedTableOptions::kHashSearch);
2366   ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
2367   ASSERT_TRUE(new_opt.hash_index_allow_collision);
2368   ASSERT_TRUE(new_opt.no_block_cache);
2369   ASSERT_TRUE(new_opt.block_cache != nullptr);
2370   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2371   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2372   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
2373   ASSERT_EQ(new_opt.block_size, 1024UL);
2374   ASSERT_EQ(new_opt.block_size_deviation, 8);
2375   ASSERT_EQ(new_opt.block_restart_interval, 4);
2376   ASSERT_EQ(new_opt.format_version, 5U);
2377   ASSERT_EQ(new_opt.whole_key_filtering, true);
2378   ASSERT_TRUE(new_opt.filter_policy != nullptr);
2379   const BloomFilterPolicy& bfp =
2380       dynamic_cast<const BloomFilterPolicy&>(*new_opt.filter_policy);
2381   EXPECT_EQ(bfp.GetMillibitsPerKey(), 4567);
2382   EXPECT_EQ(bfp.GetWholeBitsPerKey(), 5);
2383 
2384   // unknown option
2385   ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2386              "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
2387              "bad_option=1",
2388              &new_opt));
2389   ASSERT_EQ(static_cast<bool>(table_opt.cache_index_and_filter_blocks),
2390             new_opt.cache_index_and_filter_blocks);
2391   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2392 
2393   // unrecognized index type
2394   ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2395              "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX",
2396              &new_opt));
2397   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2398             new_opt.cache_index_and_filter_blocks);
2399   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2400 
2401   // unrecognized checksum type
2402   ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2403              "cache_index_and_filter_blocks=1;checksum=kxxHashXX",
2404              &new_opt));
2405   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2406             new_opt.cache_index_and_filter_blocks);
2407   ASSERT_EQ(table_opt.index_type, new_opt.index_type);
2408 
2409   // unrecognized filter policy name
2410   ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2411              "cache_index_and_filter_blocks=1;"
2412              "filter_policy=bloomfilterxx:4:true",
2413              &new_opt));
2414   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2415             new_opt.cache_index_and_filter_blocks);
2416   ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
2417 
2418   // unrecognized filter policy config
2419   ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
2420              "cache_index_and_filter_blocks=1;"
2421              "filter_policy=bloomfilter:4",
2422              &new_opt));
2423   ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
2424             new_opt.cache_index_and_filter_blocks);
2425   ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
2426 
2427   // Check block cache options are overwritten when specified
2428   // in new format as a struct.
2429   ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2430              "block_cache={capacity=1M;num_shard_bits=4;"
2431              "strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
2432              "block_cache_compressed={capacity=1M;num_shard_bits=4;"
2433              "strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
2434              &new_opt));
2435   ASSERT_TRUE(new_opt.block_cache != nullptr);
2436   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2437   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2438                 new_opt.block_cache)->GetNumShardBits(), 4);
2439   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
2440   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2441                 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
2442   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2443   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
2444   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2445                 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
2446   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
2447   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2448                 new_opt.block_cache_compressed)->GetHighPriPoolRatio(),
2449                 0.5);
2450 
2451   // Set only block cache capacity. Check other values are
2452   // reset to default values.
2453   ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2454              "block_cache={capacity=2M};"
2455              "block_cache_compressed={capacity=2M}",
2456              &new_opt));
2457   ASSERT_TRUE(new_opt.block_cache != nullptr);
2458   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL);
2459   // Default values
2460   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2461                 new_opt.block_cache)->GetNumShardBits(),
2462                 GetDefaultCacheShardBits(new_opt.block_cache->GetCapacity()));
2463   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
2464   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
2465                 ->GetHighPriPoolRatio(),
2466             0.5);
2467   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2468   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 2*1024UL*1024UL);
2469   // Default values
2470   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2471                 new_opt.block_cache_compressed)->GetNumShardBits(),
2472                 GetDefaultCacheShardBits(
2473                     new_opt.block_cache_compressed->GetCapacity()));
2474   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
2475   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2476                 ->GetHighPriPoolRatio(),
2477             0.5);
2478 
2479   // Set couple of block cache options.
2480   ASSERT_OK(GetBlockBasedTableOptionsFromString(
2481       table_opt,
2482       "block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};"
2483       "block_cache_compressed={num_shard_bits=5;"
2484       "high_pri_pool_ratio=0.0;}",
2485       &new_opt));
2486   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 0);
2487   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2488                 new_opt.block_cache)->GetNumShardBits(), 5);
2489   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false);
2490   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(
2491                 new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
2492   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2493   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 0);
2494   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2495                 new_opt.block_cache_compressed)->GetNumShardBits(), 5);
2496   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false);
2497   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2498                 ->GetHighPriPoolRatio(),
2499             0.0);
2500 
2501   // Set couple of block cache options.
2502   ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
2503              "block_cache={capacity=1M;num_shard_bits=4;"
2504              "strict_capacity_limit=true;};"
2505              "block_cache_compressed={capacity=1M;num_shard_bits=4;"
2506              "strict_capacity_limit=true;}",
2507              &new_opt));
2508   ASSERT_TRUE(new_opt.block_cache != nullptr);
2509   ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
2510   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2511                 new_opt.block_cache)->GetNumShardBits(), 4);
2512   ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true);
2513   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache)
2514                 ->GetHighPriPoolRatio(),
2515             0.5);
2516   ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
2517   ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL);
2518   ASSERT_EQ(std::dynamic_pointer_cast<ShardedCache>(
2519                 new_opt.block_cache_compressed)->GetNumShardBits(), 4);
2520   ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true);
2521   ASSERT_EQ(std::dynamic_pointer_cast<LRUCache>(new_opt.block_cache_compressed)
2522                 ->GetHighPriPoolRatio(),
2523             0.5);
2524 }
2525 
TEST_F(OptionsOldApiTest,GetPlainTableOptionsFromString)2526 TEST_F(OptionsOldApiTest, GetPlainTableOptionsFromString) {
2527   PlainTableOptions table_opt;
2528   PlainTableOptions new_opt;
2529   // make sure default values are overwritten by something else
2530   ASSERT_OK(GetPlainTableOptionsFromString(table_opt,
2531             "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2532             "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
2533             "full_scan_mode=true;store_index_in_file=true",
2534             &new_opt));
2535   ASSERT_EQ(new_opt.user_key_len, 66u);
2536   ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
2537   ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
2538   ASSERT_EQ(new_opt.index_sparseness, 8);
2539   ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
2540   ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
2541   ASSERT_TRUE(new_opt.full_scan_mode);
2542   ASSERT_TRUE(new_opt.store_index_in_file);
2543 
2544   // unknown option
2545   ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
2546              "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2547              "bad_option=1",
2548              &new_opt));
2549 
2550   // unrecognized EncodingType
2551   ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
2552              "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
2553              "encoding_type=kPrefixXX",
2554              &new_opt));
2555 }
2556 
TEST_F(OptionsOldApiTest,GetOptionsFromStringTest)2557 TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
2558   Options base_options, new_options;
2559   base_options.write_buffer_size = 20;
2560   base_options.min_write_buffer_number_to_merge = 15;
2561   BlockBasedTableOptions block_based_table_options;
2562   block_based_table_options.cache_index_and_filter_blocks = true;
2563   base_options.table_factory.reset(
2564       NewBlockBasedTableFactory(block_based_table_options));
2565 
2566   // Register an Env with object registry.
2567   ObjectLibrary::Default()->Register<Env>(
2568       "CustomEnvDefault",
2569       [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
2570          std::string* /* errmsg */) {
2571         static CustomEnv env(Env::Default());
2572         return &env;
2573       });
2574 
2575   ASSERT_OK(GetOptionsFromString(
2576       base_options,
2577       "write_buffer_size=10;max_write_buffer_number=16;"
2578       "block_based_table_factory={block_cache=1M;block_size=4;};"
2579       "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
2580       "bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files="
2581       "1;"
2582       "rate_limiter_bytes_per_sec=1024;env=CustomEnvDefault",
2583       &new_options));
2584 
2585   ASSERT_EQ(new_options.compression_opts.window_bits, 4);
2586   ASSERT_EQ(new_options.compression_opts.level, 5);
2587   ASSERT_EQ(new_options.compression_opts.strategy, 6);
2588   ASSERT_EQ(new_options.compression_opts.max_dict_bytes, 0u);
2589   ASSERT_EQ(new_options.compression_opts.zstd_max_train_bytes, 0u);
2590   ASSERT_EQ(new_options.compression_opts.parallel_threads, 1u);
2591   ASSERT_EQ(new_options.compression_opts.enabled, false);
2592   ASSERT_EQ(new_options.bottommost_compression, kDisableCompressionOption);
2593   ASSERT_EQ(new_options.bottommost_compression_opts.window_bits, 5);
2594   ASSERT_EQ(new_options.bottommost_compression_opts.level, 6);
2595   ASSERT_EQ(new_options.bottommost_compression_opts.strategy, 7);
2596   ASSERT_EQ(new_options.bottommost_compression_opts.max_dict_bytes, 0u);
2597   ASSERT_EQ(new_options.bottommost_compression_opts.zstd_max_train_bytes, 0u);
2598   ASSERT_EQ(new_options.bottommost_compression_opts.parallel_threads, 1u);
2599   ASSERT_EQ(new_options.bottommost_compression_opts.enabled, false);
2600   ASSERT_EQ(new_options.write_buffer_size, 10U);
2601   ASSERT_EQ(new_options.max_write_buffer_number, 16);
2602 
2603   auto new_block_based_table_options =
2604       new_options.table_factory->GetOptions<BlockBasedTableOptions>();
2605   ASSERT_NE(new_block_based_table_options, nullptr);
2606   ASSERT_EQ(new_block_based_table_options->block_cache->GetCapacity(),
2607             1U << 20);
2608   ASSERT_EQ(new_block_based_table_options->block_size, 4U);
2609   // don't overwrite block based table options
2610   ASSERT_TRUE(new_block_based_table_options->cache_index_and_filter_blocks);
2611 
2612   ASSERT_EQ(new_options.create_if_missing, true);
2613   ASSERT_EQ(new_options.max_open_files, 1);
2614   ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
2615   Env* newEnv = new_options.env;
2616   ASSERT_OK(Env::LoadEnv("CustomEnvDefault", &newEnv));
2617   ASSERT_EQ(newEnv, new_options.env);
2618 }
2619 
TEST_F(OptionsOldApiTest,DBOptionsSerialization)2620 TEST_F(OptionsOldApiTest, DBOptionsSerialization) {
2621   Options base_options, new_options;
2622   Random rnd(301);
2623 
2624   // Phase 1: Make big change in base_options
2625   test::RandomInitDBOptions(&base_options, &rnd);
2626 
2627   // Phase 2: obtain a string from base_option
2628   std::string base_options_file_content;
2629   ASSERT_OK(GetStringFromDBOptions(&base_options_file_content, base_options));
2630 
2631   // Phase 3: Set new_options from the derived string and expect
2632   //          new_options == base_options
2633   ASSERT_OK(GetDBOptionsFromString(DBOptions(), base_options_file_content,
2634                                    &new_options));
2635   ConfigOptions config_options;
2636   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_options, new_options));
2637 }
2638 
TEST_F(OptionsOldApiTest,ColumnFamilyOptionsSerialization)2639 TEST_F(OptionsOldApiTest, ColumnFamilyOptionsSerialization) {
2640   Options options;
2641   ColumnFamilyOptions base_opt, new_opt;
2642   Random rnd(302);
2643   // Phase 1: randomly assign base_opt
2644   // custom type options
2645   test::RandomInitCFOptions(&base_opt, options, &rnd);
2646 
2647   // Phase 2: obtain a string from base_opt
2648   std::string base_options_file_content;
2649   ASSERT_OK(
2650       GetStringFromColumnFamilyOptions(&base_options_file_content, base_opt));
2651 
2652   // Phase 3: Set new_opt from the derived string and expect
2653   //          new_opt == base_opt
2654   ASSERT_OK(GetColumnFamilyOptionsFromString(
2655       ColumnFamilyOptions(), base_options_file_content, &new_opt));
2656   ConfigOptions config_options;
2657   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_opt, new_opt));
2658   if (base_opt.compaction_filter) {
2659     delete base_opt.compaction_filter;
2660   }
2661 }
2662 #endif  // !ROCKSDB_LITE
2663 
2664 #ifndef ROCKSDB_LITE
2665 class OptionsParserTest : public testing::Test {
2666  public:
OptionsParserTest()2667   OptionsParserTest() { fs_.reset(new test::StringFS(FileSystem::Default())); }
2668 
2669  protected:
2670   std::shared_ptr<test::StringFS> fs_;
2671 };
2672 
TEST_F(OptionsParserTest,Comment)2673 TEST_F(OptionsParserTest, Comment) {
2674   DBOptions db_opt;
2675   db_opt.max_open_files = 12345;
2676   db_opt.max_background_flushes = 301;
2677   db_opt.max_total_wal_size = 1024;
2678   ColumnFamilyOptions cf_opt;
2679 
2680   std::string options_file_content =
2681       "# This is a testing option string.\n"
2682       "# Currently we only support \"#\" styled comment.\n"
2683       "\n"
2684       "[Version]\n"
2685       "  rocksdb_version=3.14.0\n"
2686       "  options_file_version=1\n"
2687       "[ DBOptions ]\n"
2688       "  # note that we don't support space around \"=\"\n"
2689       "  max_open_files=12345;\n"
2690       "  max_background_flushes=301  # comment after a statement is fine\n"
2691       "  # max_background_flushes=1000  # this line would be ignored\n"
2692       "  # max_background_compactions=2000 # so does this one\n"
2693       "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2694       "[CFOptions   \"default\"]  # column family must be specified\n"
2695       "                     # in the correct order\n"
2696       "  # if a section is blank, we will use the default\n";
2697 
2698   const std::string kTestFileName = "test-rocksdb-options.ini";
2699   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2700   RocksDBOptionsParser parser;
2701   ASSERT_OK(
2702       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2703 
2704   ConfigOptions exact;
2705   exact.input_strings_escaped = false;
2706   exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
2707   ASSERT_OK(
2708       RocksDBOptionsParser::VerifyDBOptions(exact, *parser.db_opt(), db_opt));
2709   ASSERT_EQ(parser.NumColumnFamilies(), 1U);
2710   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
2711       exact, *parser.GetCFOptions("default"), cf_opt));
2712 }
2713 
TEST_F(OptionsParserTest,ExtraSpace)2714 TEST_F(OptionsParserTest, ExtraSpace) {
2715   std::string options_file_content =
2716       "# This is a testing option string.\n"
2717       "# Currently we only support \"#\" styled comment.\n"
2718       "\n"
2719       "[      Version   ]\n"
2720       "  rocksdb_version     = 3.14.0      \n"
2721       "  options_file_version=1   # some comment\n"
2722       "[DBOptions  ]  # some comment\n"
2723       "max_open_files=12345   \n"
2724       "    max_background_flushes   =    301   \n"
2725       " max_total_wal_size     =   1024  # keep_log_file_num=1000\n"
2726       "        [CFOptions      \"default\"     ]\n"
2727       "  # if a section is blank, we will use the default\n";
2728 
2729   const std::string kTestFileName = "test-rocksdb-options.ini";
2730   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2731   RocksDBOptionsParser parser;
2732   ASSERT_OK(
2733       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2734 }
2735 
TEST_F(OptionsParserTest,MissingDBOptions)2736 TEST_F(OptionsParserTest, MissingDBOptions) {
2737   std::string options_file_content =
2738       "# This is a testing option string.\n"
2739       "# Currently we only support \"#\" styled comment.\n"
2740       "\n"
2741       "[Version]\n"
2742       "  rocksdb_version=3.14.0\n"
2743       "  options_file_version=1\n"
2744       "[CFOptions \"default\"]\n"
2745       "  # if a section is blank, we will use the default\n";
2746 
2747   const std::string kTestFileName = "test-rocksdb-options.ini";
2748   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2749   RocksDBOptionsParser parser;
2750   ASSERT_NOK(
2751       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2752   ;
2753 }
2754 
TEST_F(OptionsParserTest,DoubleDBOptions)2755 TEST_F(OptionsParserTest, DoubleDBOptions) {
2756   DBOptions db_opt;
2757   db_opt.max_open_files = 12345;
2758   db_opt.max_background_flushes = 301;
2759   db_opt.max_total_wal_size = 1024;
2760   ColumnFamilyOptions cf_opt;
2761 
2762   std::string options_file_content =
2763       "# This is a testing option string.\n"
2764       "# Currently we only support \"#\" styled comment.\n"
2765       "\n"
2766       "[Version]\n"
2767       "  rocksdb_version=3.14.0\n"
2768       "  options_file_version=1\n"
2769       "[DBOptions]\n"
2770       "  max_open_files=12345\n"
2771       "  max_background_flushes=301\n"
2772       "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2773       "[DBOptions]\n"
2774       "[CFOptions \"default\"]\n"
2775       "  # if a section is blank, we will use the default\n";
2776 
2777   const std::string kTestFileName = "test-rocksdb-options.ini";
2778   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2779   RocksDBOptionsParser parser;
2780   ASSERT_NOK(
2781       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2782 }
2783 
TEST_F(OptionsParserTest,NoDefaultCFOptions)2784 TEST_F(OptionsParserTest, NoDefaultCFOptions) {
2785   DBOptions db_opt;
2786   db_opt.max_open_files = 12345;
2787   db_opt.max_background_flushes = 301;
2788   db_opt.max_total_wal_size = 1024;
2789   ColumnFamilyOptions cf_opt;
2790 
2791   std::string options_file_content =
2792       "# This is a testing option string.\n"
2793       "# Currently we only support \"#\" styled comment.\n"
2794       "\n"
2795       "[Version]\n"
2796       "  rocksdb_version=3.14.0\n"
2797       "  options_file_version=1\n"
2798       "[DBOptions]\n"
2799       "  max_open_files=12345\n"
2800       "  max_background_flushes=301\n"
2801       "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2802       "[CFOptions \"something_else\"]\n"
2803       "  # if a section is blank, we will use the default\n";
2804 
2805   const std::string kTestFileName = "test-rocksdb-options.ini";
2806   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2807   RocksDBOptionsParser parser;
2808   ASSERT_NOK(
2809       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2810 }
2811 
TEST_F(OptionsParserTest,DefaultCFOptionsMustBeTheFirst)2812 TEST_F(OptionsParserTest, DefaultCFOptionsMustBeTheFirst) {
2813   DBOptions db_opt;
2814   db_opt.max_open_files = 12345;
2815   db_opt.max_background_flushes = 301;
2816   db_opt.max_total_wal_size = 1024;
2817   ColumnFamilyOptions cf_opt;
2818 
2819   std::string options_file_content =
2820       "# This is a testing option string.\n"
2821       "# Currently we only support \"#\" styled comment.\n"
2822       "\n"
2823       "[Version]\n"
2824       "  rocksdb_version=3.14.0\n"
2825       "  options_file_version=1\n"
2826       "[DBOptions]\n"
2827       "  max_open_files=12345\n"
2828       "  max_background_flushes=301\n"
2829       "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2830       "[CFOptions \"something_else\"]\n"
2831       "  # if a section is blank, we will use the default\n"
2832       "[CFOptions \"default\"]\n"
2833       "  # if a section is blank, we will use the default\n";
2834 
2835   const std::string kTestFileName = "test-rocksdb-options.ini";
2836   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2837   RocksDBOptionsParser parser;
2838   ASSERT_NOK(
2839       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2840 }
2841 
TEST_F(OptionsParserTest,DuplicateCFOptions)2842 TEST_F(OptionsParserTest, DuplicateCFOptions) {
2843   DBOptions db_opt;
2844   db_opt.max_open_files = 12345;
2845   db_opt.max_background_flushes = 301;
2846   db_opt.max_total_wal_size = 1024;
2847   ColumnFamilyOptions cf_opt;
2848 
2849   std::string options_file_content =
2850       "# This is a testing option string.\n"
2851       "# Currently we only support \"#\" styled comment.\n"
2852       "\n"
2853       "[Version]\n"
2854       "  rocksdb_version=3.14.0\n"
2855       "  options_file_version=1\n"
2856       "[DBOptions]\n"
2857       "  max_open_files=12345\n"
2858       "  max_background_flushes=301\n"
2859       "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2860       "[CFOptions \"default\"]\n"
2861       "[CFOptions \"something_else\"]\n"
2862       "[CFOptions \"something_else\"]\n";
2863 
2864   const std::string kTestFileName = "test-rocksdb-options.ini";
2865   ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2866   RocksDBOptionsParser parser;
2867   ASSERT_NOK(
2868       parser.Parse(kTestFileName, fs_.get(), false, 4096 /* readahead_size */));
2869 }
2870 
TEST_F(OptionsParserTest,IgnoreUnknownOptions)2871 TEST_F(OptionsParserTest, IgnoreUnknownOptions) {
2872   for (int case_id = 0; case_id < 5; case_id++) {
2873     DBOptions db_opt;
2874     db_opt.max_open_files = 12345;
2875     db_opt.max_background_flushes = 301;
2876     db_opt.max_total_wal_size = 1024;
2877     ColumnFamilyOptions cf_opt;
2878 
2879     std::string version_string;
2880     bool should_ignore = true;
2881     if (case_id == 0) {
2882       // same version
2883       should_ignore = false;
2884       version_string =
2885           ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR) + ".0";
2886     } else if (case_id == 1) {
2887       // higher minor version
2888       should_ignore = true;
2889       version_string =
2890           ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR + 1) + ".0";
2891     } else if (case_id == 2) {
2892       // higher major version.
2893       should_ignore = true;
2894       version_string = ToString(ROCKSDB_MAJOR + 1) + ".0.0";
2895     } else if (case_id == 3) {
2896       // lower minor version
2897 #if ROCKSDB_MINOR == 0
2898       continue;
2899 #else
2900       version_string =
2901           ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR - 1) + ".0";
2902       should_ignore = false;
2903 #endif
2904     } else {
2905       // lower major version
2906       should_ignore = false;
2907       version_string =
2908           ToString(ROCKSDB_MAJOR - 1) + "." + ToString(ROCKSDB_MINOR) + ".0";
2909     }
2910 
2911     std::string options_file_content =
2912         "# This is a testing option string.\n"
2913         "# Currently we only support \"#\" styled comment.\n"
2914         "\n"
2915         "[Version]\n"
2916         "  rocksdb_version=" +
2917         version_string +
2918         "\n"
2919         "  options_file_version=1\n"
2920         "[DBOptions]\n"
2921         "  max_open_files=12345\n"
2922         "  max_background_flushes=301\n"
2923         "  max_total_wal_size=1024  # keep_log_file_num=1000\n"
2924         "  unknown_db_option1=321\n"
2925         "  unknown_db_option2=false\n"
2926         "[CFOptions \"default\"]\n"
2927         "  unknown_cf_option1=hello\n"
2928         "[CFOptions \"something_else\"]\n"
2929         "  unknown_cf_option2=world\n"
2930         "  # if a section is blank, we will use the default\n";
2931 
2932     const std::string kTestFileName = "test-rocksdb-options.ini";
2933     auto s = fs_->FileExists(kTestFileName, IOOptions(), nullptr);
2934     ASSERT_TRUE(s.ok() || s.IsNotFound());
2935     if (s.ok()) {
2936       ASSERT_OK(fs_->DeleteFile(kTestFileName, IOOptions(), nullptr));
2937     }
2938     ASSERT_OK(fs_->WriteToNewFile(kTestFileName, options_file_content));
2939     RocksDBOptionsParser parser;
2940     ASSERT_NOK(parser.Parse(kTestFileName, fs_.get(), false,
2941                             4096 /* readahead_size */));
2942     if (should_ignore) {
2943       ASSERT_OK(parser.Parse(kTestFileName, fs_.get(),
2944                              true /* ignore_unknown_options */,
2945                              4096 /* readahead_size */));
2946     } else {
2947       ASSERT_NOK(parser.Parse(kTestFileName, fs_.get(),
2948                               true /* ignore_unknown_options */,
2949                               4096 /* readahead_size */));
2950     }
2951   }
2952 }
2953 
TEST_F(OptionsParserTest,ParseVersion)2954 TEST_F(OptionsParserTest, ParseVersion) {
2955   DBOptions db_opt;
2956   db_opt.max_open_files = 12345;
2957   db_opt.max_background_flushes = 301;
2958   db_opt.max_total_wal_size = 1024;
2959   ColumnFamilyOptions cf_opt;
2960 
2961   std::string file_template =
2962       "# This is a testing option string.\n"
2963       "# Currently we only support \"#\" styled comment.\n"
2964       "\n"
2965       "[Version]\n"
2966       "  rocksdb_version=3.13.1\n"
2967       "  options_file_version=%s\n"
2968       "[DBOptions]\n"
2969       "[CFOptions \"default\"]\n";
2970   const int kLength = 1000;
2971   char buffer[kLength];
2972   RocksDBOptionsParser parser;
2973 
2974   const std::vector<std::string> invalid_versions = {
2975       "a.b.c", "3.2.2b", "3.-12", "3. 1",  // only digits and dots are allowed
2976       "1.2.3.4",
2977       "1.2.3"  // can only contains at most one dot.
2978       "0",     // options_file_version must be at least one
2979       "3..2",
2980       ".", ".1.2",             // must have at least one digit before each dot
2981       "1.2.", "1.", "2.34."};  // must have at least one digit after each dot
2982   for (auto iv : invalid_versions) {
2983     snprintf(buffer, kLength - 1, file_template.c_str(), iv.c_str());
2984 
2985     parser.Reset();
2986     ASSERT_OK(fs_->WriteToNewFile(iv, buffer));
2987     ASSERT_NOK(parser.Parse(iv, fs_.get(), false, 0 /* readahead_size */));
2988   }
2989 
2990   const std::vector<std::string> valid_versions = {
2991       "1.232", "100", "3.12", "1", "12.3  ", "  1.25  "};
2992   for (auto vv : valid_versions) {
2993     snprintf(buffer, kLength - 1, file_template.c_str(), vv.c_str());
2994     parser.Reset();
2995     ASSERT_OK(fs_->WriteToNewFile(vv, buffer));
2996     ASSERT_OK(parser.Parse(vv, fs_.get(), false, 0 /* readahead_size */));
2997   }
2998 }
2999 
VerifyCFPointerTypedOptions(ColumnFamilyOptions * base_cf_opt,const ColumnFamilyOptions * new_cf_opt,const std::unordered_map<std::string,std::string> * new_cf_opt_map)3000 void VerifyCFPointerTypedOptions(
3001     ColumnFamilyOptions* base_cf_opt, const ColumnFamilyOptions* new_cf_opt,
3002     const std::unordered_map<std::string, std::string>* new_cf_opt_map) {
3003   std::string name_buffer;
3004   ConfigOptions config_options;
3005   config_options.input_strings_escaped = false;
3006   ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, *base_cf_opt,
3007                                                   *new_cf_opt, new_cf_opt_map));
3008 
3009   // change the name of merge operator back-and-forth
3010   {
3011     auto* merge_operator = dynamic_cast<test::ChanglingMergeOperator*>(
3012         base_cf_opt->merge_operator.get());
3013     if (merge_operator != nullptr) {
3014       name_buffer = merge_operator->Name();
3015       // change the name  and expect non-ok status
3016       merge_operator->SetName("some-other-name");
3017       ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3018           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3019       // change the name back and expect ok status
3020       merge_operator->SetName(name_buffer);
3021       ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3022           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3023     }
3024   }
3025 
3026   // change the name of the compaction filter factory back-and-forth
3027   {
3028     auto* compaction_filter_factory =
3029         dynamic_cast<test::ChanglingCompactionFilterFactory*>(
3030             base_cf_opt->compaction_filter_factory.get());
3031     if (compaction_filter_factory != nullptr) {
3032       name_buffer = compaction_filter_factory->Name();
3033       // change the name and expect non-ok status
3034       compaction_filter_factory->SetName("some-other-name");
3035       ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3036           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3037       // change the name back and expect ok status
3038       compaction_filter_factory->SetName(name_buffer);
3039       ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3040           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3041     }
3042   }
3043 
3044   // test by setting compaction_filter to nullptr
3045   {
3046     auto* tmp_compaction_filter = base_cf_opt->compaction_filter;
3047     if (tmp_compaction_filter != nullptr) {
3048       base_cf_opt->compaction_filter = nullptr;
3049       // set compaction_filter to nullptr and expect non-ok status
3050       ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3051           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3052       // set the value back and expect ok status
3053       base_cf_opt->compaction_filter = tmp_compaction_filter;
3054       ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3055           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3056     }
3057   }
3058 
3059   // test by setting table_factory to nullptr
3060   {
3061     auto tmp_table_factory = base_cf_opt->table_factory;
3062     if (tmp_table_factory != nullptr) {
3063       base_cf_opt->table_factory.reset();
3064       // set table_factory to nullptr and expect non-ok status
3065       ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3066           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3067       // set the value back and expect ok status
3068       base_cf_opt->table_factory = tmp_table_factory;
3069       ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3070           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3071     }
3072   }
3073 
3074   // test by setting memtable_factory to nullptr
3075   {
3076     auto tmp_memtable_factory = base_cf_opt->memtable_factory;
3077     if (tmp_memtable_factory != nullptr) {
3078       base_cf_opt->memtable_factory.reset();
3079       // set memtable_factory to nullptr and expect non-ok status
3080       ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
3081           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3082       // set the value back and expect ok status
3083       base_cf_opt->memtable_factory = tmp_memtable_factory;
3084       ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3085           config_options, *base_cf_opt, *new_cf_opt, new_cf_opt_map));
3086     }
3087   }
3088 }
3089 
TEST_F(OptionsParserTest,Readahead)3090 TEST_F(OptionsParserTest, Readahead) {
3091   DBOptions base_db_opt;
3092   std::vector<ColumnFamilyOptions> base_cf_opts;
3093   base_cf_opts.emplace_back();
3094   base_cf_opts.emplace_back();
3095 
3096   std::string one_mb_string = std::string(1024 * 1024, 'x');
3097   std::vector<std::string> cf_names = {"default", one_mb_string};
3098   const std::string kOptionsFileName = "test-persisted-options.ini";
3099 
3100   ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
3101                                   kOptionsFileName, fs_.get()));
3102 
3103   uint64_t file_size = 0;
3104   ASSERT_OK(
3105       fs_->GetFileSize(kOptionsFileName, IOOptions(), &file_size, nullptr));
3106   assert(file_size > 0);
3107 
3108   RocksDBOptionsParser parser;
3109 
3110   fs_->num_seq_file_read_ = 0;
3111   size_t readahead_size = 128 * 1024;
3112 
3113   ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false, readahead_size));
3114   ASSERT_EQ(fs_->num_seq_file_read_.load(),
3115             (file_size - 1) / readahead_size + 1);
3116 
3117   fs_->num_seq_file_read_.store(0);
3118   readahead_size = 1024 * 1024;
3119   ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false, readahead_size));
3120   ASSERT_EQ(fs_->num_seq_file_read_.load(),
3121             (file_size - 1) / readahead_size + 1);
3122 
3123   // Tiny readahead. 8 KB is read each time.
3124   fs_->num_seq_file_read_.store(0);
3125   ASSERT_OK(
3126       parser.Parse(kOptionsFileName, fs_.get(), false, 1 /* readahead_size */));
3127   ASSERT_GE(fs_->num_seq_file_read_.load(), file_size / (8 * 1024));
3128   ASSERT_LT(fs_->num_seq_file_read_.load(), file_size / (8 * 1024) * 2);
3129 
3130   // Disable readahead means 512KB readahead.
3131   fs_->num_seq_file_read_.store(0);
3132   ASSERT_OK(
3133       parser.Parse(kOptionsFileName, fs_.get(), false, 0 /* readahead_size */));
3134   ASSERT_GE(fs_->num_seq_file_read_.load(), (file_size - 1) / (512 * 1024) + 1);
3135 }
3136 
TEST_F(OptionsParserTest,DumpAndParse)3137 TEST_F(OptionsParserTest, DumpAndParse) {
3138   DBOptions base_db_opt;
3139   std::vector<ColumnFamilyOptions> base_cf_opts;
3140   std::vector<std::string> cf_names = {"default", "cf1", "cf2", "cf3",
3141                                        "c:f:4:4:4"
3142                                        "p\\i\\k\\a\\chu\\\\\\",
3143                                        "###rocksdb#1-testcf#2###"};
3144   const int num_cf = static_cast<int>(cf_names.size());
3145   Random rnd(302);
3146   test::RandomInitDBOptions(&base_db_opt, &rnd);
3147   base_db_opt.db_log_dir += "/#odd #but #could #happen #path #/\\\\#OMG";
3148 
3149   BlockBasedTableOptions special_bbto;
3150   special_bbto.cache_index_and_filter_blocks = true;
3151   special_bbto.block_size = 999999;
3152 
3153   for (int c = 0; c < num_cf; ++c) {
3154     ColumnFamilyOptions cf_opt;
3155     Random cf_rnd(0xFB + c);
3156     test::RandomInitCFOptions(&cf_opt, base_db_opt, &cf_rnd);
3157     if (c < 4) {
3158       cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c));
3159     }
3160     if (c < 3) {
3161       cf_opt.table_factory.reset(test::RandomTableFactory(&rnd, c));
3162     } else if (c == 4) {
3163       cf_opt.table_factory.reset(NewBlockBasedTableFactory(special_bbto));
3164     }
3165     base_cf_opts.emplace_back(cf_opt);
3166   }
3167 
3168   const std::string kOptionsFileName = "test-persisted-options.ini";
3169   // Use default for escaped(true), unknown(false) and check (exact)
3170   ConfigOptions config_options;
3171   ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
3172                                   kOptionsFileName, fs_.get()));
3173 
3174   RocksDBOptionsParser parser;
3175   ASSERT_OK(parser.Parse(config_options, kOptionsFileName, fs_.get()));
3176 
3177   // Make sure block-based table factory options was deserialized correctly
3178   std::shared_ptr<TableFactory> ttf = (*parser.cf_opts())[4].table_factory;
3179   ASSERT_EQ(TableFactory::kBlockBasedTableName(), std::string(ttf->Name()));
3180   const auto parsed_bbto = ttf->GetOptions<BlockBasedTableOptions>();
3181   ASSERT_NE(parsed_bbto, nullptr);
3182   ASSERT_EQ(special_bbto.block_size, parsed_bbto->block_size);
3183   ASSERT_EQ(special_bbto.cache_index_and_filter_blocks,
3184             parsed_bbto->cache_index_and_filter_blocks);
3185 
3186   ASSERT_OK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3187       config_options, base_db_opt, cf_names, base_cf_opts, kOptionsFileName,
3188       fs_.get()));
3189 
3190   ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(
3191       config_options, *parser.db_opt(), base_db_opt));
3192   for (int c = 0; c < num_cf; ++c) {
3193     const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
3194     ASSERT_NE(cf_opt, nullptr);
3195     ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
3196         config_options, base_cf_opts[c], *cf_opt,
3197         &(parser.cf_opt_maps()->at(c))));
3198   }
3199 
3200   // Further verify pointer-typed options
3201   for (int c = 0; c < num_cf; ++c) {
3202     const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
3203     ASSERT_NE(cf_opt, nullptr);
3204     VerifyCFPointerTypedOptions(&base_cf_opts[c], cf_opt,
3205                                 &(parser.cf_opt_maps()->at(c)));
3206   }
3207 
3208   ASSERT_EQ(parser.GetCFOptions("does not exist"), nullptr);
3209 
3210   base_db_opt.max_open_files++;
3211   ASSERT_NOK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3212       config_options, base_db_opt, cf_names, base_cf_opts, kOptionsFileName,
3213       fs_.get()));
3214 
3215   for (int c = 0; c < num_cf; ++c) {
3216     if (base_cf_opts[c].compaction_filter) {
3217       delete base_cf_opts[c].compaction_filter;
3218     }
3219   }
3220 }
3221 
TEST_F(OptionsParserTest,DifferentDefault)3222 TEST_F(OptionsParserTest, DifferentDefault) {
3223   const std::string kOptionsFileName = "test-persisted-options.ini";
3224 
3225   ColumnFamilyOptions cf_level_opts;
3226   ASSERT_EQ(CompactionPri::kMinOverlappingRatio, cf_level_opts.compaction_pri);
3227   cf_level_opts.OptimizeLevelStyleCompaction();
3228 
3229   ColumnFamilyOptions cf_univ_opts;
3230   cf_univ_opts.OptimizeUniversalStyleCompaction();
3231 
3232   ASSERT_OK(PersistRocksDBOptions(DBOptions(), {"default", "universal"},
3233                                   {cf_level_opts, cf_univ_opts},
3234                                   kOptionsFileName, fs_.get()));
3235 
3236   RocksDBOptionsParser parser;
3237   ASSERT_OK(parser.Parse(kOptionsFileName, fs_.get(), false,
3238                          4096 /* readahead_size */));
3239 
3240   {
3241     Options old_default_opts;
3242     old_default_opts.OldDefaults();
3243     ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3244     ASSERT_EQ(5000, old_default_opts.max_open_files);
3245     ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3246     ASSERT_EQ(WALRecoveryMode::kTolerateCorruptedTailRecords,
3247               old_default_opts.wal_recovery_mode);
3248   }
3249   {
3250     Options old_default_opts;
3251     old_default_opts.OldDefaults(4, 6);
3252     ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3253     ASSERT_EQ(5000, old_default_opts.max_open_files);
3254   }
3255   {
3256     Options old_default_opts;
3257     old_default_opts.OldDefaults(4, 7);
3258     ASSERT_NE(10 * 1048576, old_default_opts.max_bytes_for_level_base);
3259     ASSERT_NE(4, old_default_opts.table_cache_numshardbits);
3260     ASSERT_EQ(5000, old_default_opts.max_open_files);
3261     ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3262   }
3263   {
3264     ColumnFamilyOptions old_default_cf_opts;
3265     old_default_cf_opts.OldDefaults();
3266     ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3267     ASSERT_EQ(4 << 20, old_default_cf_opts.write_buffer_size);
3268     ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3269     ASSERT_EQ(0, old_default_cf_opts.soft_pending_compaction_bytes_limit);
3270     ASSERT_EQ(0, old_default_cf_opts.hard_pending_compaction_bytes_limit);
3271     ASSERT_EQ(CompactionPri::kByCompensatedSize,
3272               old_default_cf_opts.compaction_pri);
3273   }
3274   {
3275     ColumnFamilyOptions old_default_cf_opts;
3276     old_default_cf_opts.OldDefaults(4, 6);
3277     ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
3278     ASSERT_EQ(CompactionPri::kByCompensatedSize,
3279               old_default_cf_opts.compaction_pri);
3280   }
3281   {
3282     ColumnFamilyOptions old_default_cf_opts;
3283     old_default_cf_opts.OldDefaults(4, 7);
3284     ASSERT_NE(2 * 1048576, old_default_cf_opts.target_file_size_base);
3285     ASSERT_EQ(CompactionPri::kByCompensatedSize,
3286               old_default_cf_opts.compaction_pri);
3287   }
3288   {
3289     Options old_default_opts;
3290     old_default_opts.OldDefaults(5, 1);
3291     ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3292   }
3293   {
3294     Options old_default_opts;
3295     old_default_opts.OldDefaults(5, 2);
3296     ASSERT_EQ(16 * 1024U * 1024U, old_default_opts.delayed_write_rate);
3297     ASSERT_TRUE(old_default_opts.compaction_pri ==
3298                 CompactionPri::kByCompensatedSize);
3299   }
3300   {
3301     Options old_default_opts;
3302     old_default_opts.OldDefaults(5, 18);
3303     ASSERT_TRUE(old_default_opts.compaction_pri ==
3304                 CompactionPri::kByCompensatedSize);
3305   }
3306 
3307   Options small_opts;
3308   small_opts.OptimizeForSmallDb();
3309   ASSERT_EQ(2 << 20, small_opts.write_buffer_size);
3310   ASSERT_EQ(5000, small_opts.max_open_files);
3311 }
3312 
3313 class OptionsSanityCheckTest : public OptionsParserTest {
3314  public:
OptionsSanityCheckTest()3315   OptionsSanityCheckTest() {}
3316 
3317  protected:
SanityCheckCFOptions(const ColumnFamilyOptions & cf_opts,ConfigOptions::SanityLevel level,bool input_strings_escaped=true)3318   Status SanityCheckCFOptions(const ColumnFamilyOptions& cf_opts,
3319                               ConfigOptions::SanityLevel level,
3320                               bool input_strings_escaped = true) {
3321     ConfigOptions config_options;
3322     config_options.sanity_level = level;
3323     config_options.ignore_unknown_options = false;
3324     config_options.input_strings_escaped = input_strings_escaped;
3325 
3326     return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
3327         config_options, DBOptions(), {"default"}, {cf_opts}, kOptionsFileName,
3328         fs_.get());
3329   }
3330 
PersistCFOptions(const ColumnFamilyOptions & cf_opts)3331   Status PersistCFOptions(const ColumnFamilyOptions& cf_opts) {
3332     Status s = fs_->DeleteFile(kOptionsFileName, IOOptions(), nullptr);
3333     if (!s.ok()) {
3334       return s;
3335     }
3336     return PersistRocksDBOptions(DBOptions(), {"default"}, {cf_opts},
3337                                  kOptionsFileName, fs_.get());
3338   }
3339 
3340   const std::string kOptionsFileName = "OPTIONS";
3341 };
3342 
TEST_F(OptionsSanityCheckTest,SanityCheck)3343 TEST_F(OptionsSanityCheckTest, SanityCheck) {
3344   ColumnFamilyOptions opts;
3345   Random rnd(301);
3346 
3347   // default ColumnFamilyOptions
3348   {
3349     ASSERT_OK(PersistCFOptions(opts));
3350     ASSERT_OK(
3351         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3352   }
3353 
3354   // prefix_extractor
3355   {
3356     // Okay to change prefix_extractor form nullptr to non-nullptr
3357     ASSERT_EQ(opts.prefix_extractor.get(), nullptr);
3358     opts.prefix_extractor.reset(NewCappedPrefixTransform(10));
3359     ASSERT_OK(SanityCheckCFOptions(
3360         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3361     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3362 
3363     // persist the change
3364     ASSERT_OK(PersistCFOptions(opts));
3365     ASSERT_OK(
3366         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3367 
3368     // use same prefix extractor but with different parameter
3369     opts.prefix_extractor.reset(NewCappedPrefixTransform(15));
3370     // expect pass only in
3371     // ConfigOptions::kSanityLevelLooselyCompatible
3372     ASSERT_NOK(
3373         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3374     ASSERT_OK(SanityCheckCFOptions(
3375         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3376     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3377 
3378     // repeat the test with FixedPrefixTransform
3379     opts.prefix_extractor.reset(NewFixedPrefixTransform(10));
3380     ASSERT_NOK(
3381         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3382     ASSERT_OK(SanityCheckCFOptions(
3383         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3384     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3385 
3386     // persist the change of prefix_extractor
3387     ASSERT_OK(PersistCFOptions(opts));
3388     ASSERT_OK(
3389         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3390 
3391     // use same prefix extractor but with different parameter
3392     opts.prefix_extractor.reset(NewFixedPrefixTransform(15));
3393     // expect pass only in
3394     // ConfigOptions::kSanityLevelLooselyCompatible
3395     ASSERT_NOK(
3396         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3397     ASSERT_OK(SanityCheckCFOptions(
3398         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3399     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3400 
3401     // Change prefix extractor from non-nullptr to nullptr
3402     opts.prefix_extractor.reset();
3403     // expect pass as it's safe to change prefix_extractor
3404     // from non-null to null
3405     ASSERT_OK(SanityCheckCFOptions(
3406         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3407     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3408   }
3409   // persist the change
3410   ASSERT_OK(PersistCFOptions(opts));
3411   ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3412 
3413   // table_factory
3414   {
3415     for (int tb = 0; tb <= 2; ++tb) {
3416       // change the table factory
3417       opts.table_factory.reset(test::RandomTableFactory(&rnd, tb));
3418       ASSERT_NOK(SanityCheckCFOptions(
3419           opts, ConfigOptions::kSanityLevelLooselyCompatible));
3420       ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3421 
3422       // persist the change
3423       ASSERT_OK(PersistCFOptions(opts));
3424       ASSERT_OK(
3425           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3426     }
3427   }
3428 
3429   // merge_operator
3430   {
3431     // Test when going from nullptr -> merge operator
3432     opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
3433     ASSERT_OK(SanityCheckCFOptions(
3434         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3435     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3436 
3437     // persist the change
3438     ASSERT_OK(PersistCFOptions(opts));
3439     ASSERT_OK(
3440         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3441 
3442     for (int test = 0; test < 5; ++test) {
3443       // change the merge operator
3444       opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
3445       ASSERT_NOK(SanityCheckCFOptions(
3446           opts, ConfigOptions::kSanityLevelLooselyCompatible));
3447       ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3448 
3449       // persist the change
3450       ASSERT_OK(PersistCFOptions(opts));
3451       ASSERT_OK(
3452           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3453     }
3454 
3455     // Test when going from merge operator -> nullptr
3456     opts.merge_operator = nullptr;
3457     ASSERT_NOK(SanityCheckCFOptions(
3458         opts, ConfigOptions::kSanityLevelLooselyCompatible));
3459     ASSERT_OK(SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelNone));
3460 
3461     // persist the change
3462     ASSERT_OK(PersistCFOptions(opts));
3463     ASSERT_OK(
3464         SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3465   }
3466 
3467   // compaction_filter
3468   {
3469     for (int test = 0; test < 5; ++test) {
3470       // change the compaction filter
3471       opts.compaction_filter = test::RandomCompactionFilter(&rnd);
3472       ASSERT_NOK(
3473           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3474       ASSERT_OK(SanityCheckCFOptions(
3475           opts, ConfigOptions::kSanityLevelLooselyCompatible));
3476 
3477       // persist the change
3478       ASSERT_OK(PersistCFOptions(opts));
3479       ASSERT_OK(
3480           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3481       delete opts.compaction_filter;
3482       opts.compaction_filter = nullptr;
3483     }
3484   }
3485 
3486   // compaction_filter_factory
3487   {
3488     for (int test = 0; test < 5; ++test) {
3489       // change the compaction filter factory
3490       opts.compaction_filter_factory.reset(
3491           test::RandomCompactionFilterFactory(&rnd));
3492       ASSERT_NOK(
3493           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3494       ASSERT_OK(SanityCheckCFOptions(
3495           opts, ConfigOptions::kSanityLevelLooselyCompatible));
3496 
3497       // persist the change
3498       ASSERT_OK(PersistCFOptions(opts));
3499       ASSERT_OK(
3500           SanityCheckCFOptions(opts, ConfigOptions::kSanityLevelExactMatch));
3501     }
3502   }
3503 }
3504 
3505 namespace {
IsEscapedString(const std::string & str)3506 bool IsEscapedString(const std::string& str) {
3507   for (size_t i = 0; i < str.size(); ++i) {
3508     if (str[i] == '\\') {
3509       // since we already handle those two consecutive '\'s in
3510       // the next if-then branch, any '\' appear at the end
3511       // of an escaped string in such case is not valid.
3512       if (i == str.size() - 1) {
3513         return false;
3514       }
3515       if (str[i + 1] == '\\') {
3516         // if there're two consecutive '\'s, skip the second one.
3517         i++;
3518         continue;
3519       }
3520       switch (str[i + 1]) {
3521         case ':':
3522         case '\\':
3523         case '#':
3524           continue;
3525         default:
3526           // if true, '\' together with str[i + 1] is not a valid escape.
3527           if (UnescapeChar(str[i + 1]) == str[i + 1]) {
3528             return false;
3529           }
3530       }
3531     } else if (isSpecialChar(str[i]) && (i == 0 || str[i - 1] != '\\')) {
3532       return false;
3533     }
3534   }
3535   return true;
3536 }
3537 }  // namespace
3538 
TEST_F(OptionsParserTest,IntegerParsing)3539 TEST_F(OptionsParserTest, IntegerParsing) {
3540   ASSERT_EQ(ParseUint64("18446744073709551615"), 18446744073709551615U);
3541   ASSERT_EQ(ParseUint32("4294967295"), 4294967295U);
3542   ASSERT_EQ(ParseSizeT("18446744073709551615"), 18446744073709551615U);
3543   ASSERT_EQ(ParseInt64("9223372036854775807"), 9223372036854775807);
3544   ASSERT_EQ(ParseInt64("-9223372036854775808"), port::kMinInt64);
3545   ASSERT_EQ(ParseInt32("2147483647"), 2147483647);
3546   ASSERT_EQ(ParseInt32("-2147483648"), port::kMinInt32);
3547   ASSERT_EQ(ParseInt("-32767"), -32767);
3548   ASSERT_EQ(ParseDouble("-1.234567"), -1.234567);
3549 }
3550 
TEST_F(OptionsParserTest,EscapeOptionString)3551 TEST_F(OptionsParserTest, EscapeOptionString) {
3552   ASSERT_EQ(UnescapeOptionString(
3553                 "This is a test string with \\# \\: and \\\\ escape chars."),
3554             "This is a test string with # : and \\ escape chars.");
3555 
3556   ASSERT_EQ(
3557       EscapeOptionString("This is a test string with # : and \\ escape chars."),
3558       "This is a test string with \\# \\: and \\\\ escape chars.");
3559 
3560   std::string readible_chars =
3561       "A String like this \"1234567890-=_)(*&^%$#@!ertyuiop[]{POIU"
3562       "YTREWQasdfghjkl;':LKJHGFDSAzxcvbnm,.?>"
3563       "<MNBVCXZ\\\" should be okay to \\#\\\\\\:\\#\\#\\#\\ "
3564       "be serialized and deserialized";
3565 
3566   std::string escaped_string = EscapeOptionString(readible_chars);
3567   ASSERT_TRUE(IsEscapedString(escaped_string));
3568   // This two transformations should be canceled and should output
3569   // the original input.
3570   ASSERT_EQ(UnescapeOptionString(escaped_string), readible_chars);
3571 
3572   std::string all_chars;
3573   for (unsigned char c = 0;; ++c) {
3574     all_chars += c;
3575     if (c == 255) {
3576       break;
3577     }
3578   }
3579   escaped_string = EscapeOptionString(all_chars);
3580   ASSERT_TRUE(IsEscapedString(escaped_string));
3581   ASSERT_EQ(UnescapeOptionString(escaped_string), all_chars);
3582 
3583   ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
3584                 "     A simple statement with a comment.  # like this :)"),
3585             "A simple statement with a comment.");
3586 
3587   ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
3588                 "Escape \\# and # comment together   ."),
3589             "Escape \\# and");
3590 }
3591 
TestAndCompareOption(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,void * base_ptr,void * comp_ptr)3592 static void TestAndCompareOption(const ConfigOptions& config_options,
3593                                  const OptionTypeInfo& opt_info,
3594                                  const std::string& opt_name, void* base_ptr,
3595                                  void* comp_ptr) {
3596   std::string result, mismatch;
3597   ASSERT_OK(opt_info.Serialize(config_options, opt_name, base_ptr, &result));
3598   ASSERT_OK(opt_info.Parse(config_options, opt_name, result, comp_ptr));
3599   ASSERT_TRUE(opt_info.AreEqual(config_options, opt_name, base_ptr, comp_ptr,
3600                                 &mismatch));
3601 }
3602 
TestAndCompareOption(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,const std::string & opt_value,void * base_ptr,void * comp_ptr)3603 static void TestAndCompareOption(const ConfigOptions& config_options,
3604                                  const OptionTypeInfo& opt_info,
3605                                  const std::string& opt_name,
3606                                  const std::string& opt_value, void* base_ptr,
3607                                  void* comp_ptr) {
3608   ASSERT_OK(opt_info.Parse(config_options, opt_name, opt_value, base_ptr));
3609   TestAndCompareOption(config_options, opt_info, opt_name, base_ptr, comp_ptr);
3610 }
3611 
3612 template <typename T>
TestOptInfo(const ConfigOptions & config_options,OptionType opt_type,T * base,T * comp)3613 void TestOptInfo(const ConfigOptions& config_options, OptionType opt_type,
3614                  T* base, T* comp) {
3615   std::string result;
3616   OptionTypeInfo opt_info(0, opt_type);
3617   ASSERT_FALSE(opt_info.AreEqual(config_options, "base", base, comp, &result));
3618   ASSERT_EQ(result, "base");
3619   ASSERT_NE(*base, *comp);
3620   TestAndCompareOption(config_options, opt_info, "base", base, comp);
3621   ASSERT_EQ(*base, *comp);
3622 }
3623 
3624 class OptionTypeInfoTest : public testing::Test {};
3625 
TEST_F(OptionTypeInfoTest,BasicTypes)3626 TEST_F(OptionTypeInfoTest, BasicTypes) {
3627   ConfigOptions config_options;
3628   {
3629     bool a = true, b = false;
3630     TestOptInfo(config_options, OptionType::kBoolean, &a, &b);
3631   }
3632   {
3633     int a = 100, b = 200;
3634     TestOptInfo(config_options, OptionType::kInt, &a, &b);
3635   }
3636   {
3637     int32_t a = 100, b = 200;
3638     TestOptInfo(config_options, OptionType::kInt32T, &a, &b);
3639   }
3640   {
3641     int64_t a = 100, b = 200;
3642     TestOptInfo(config_options, OptionType::kInt64T, &a, &b);
3643   }
3644   {
3645     unsigned int a = 100, b = 200;
3646     TestOptInfo(config_options, OptionType::kUInt, &a, &b);
3647   }
3648   {
3649     uint32_t a = 100, b = 200;
3650     TestOptInfo(config_options, OptionType::kUInt32T, &a, &b);
3651   }
3652   {
3653     uint64_t a = 100, b = 200;
3654     TestOptInfo(config_options, OptionType::kUInt64T, &a, &b);
3655   }
3656   {
3657     size_t a = 100, b = 200;
3658     TestOptInfo(config_options, OptionType::kSizeT, &a, &b);
3659   }
3660   {
3661     std::string a = "100", b = "200";
3662     TestOptInfo(config_options, OptionType::kString, &a, &b);
3663   }
3664   {
3665     double a = 1.0, b = 2.0;
3666     TestOptInfo(config_options, OptionType::kDouble, &a, &b);
3667   }
3668 }
3669 
TEST_F(OptionTypeInfoTest,TestInvalidArgs)3670 TEST_F(OptionTypeInfoTest, TestInvalidArgs) {
3671   ConfigOptions config_options;
3672   bool b;
3673   int i;
3674   int32_t i32;
3675   int64_t i64;
3676   unsigned int u;
3677   int32_t u32;
3678   int64_t u64;
3679   size_t sz;
3680   double d;
3681 
3682   ASSERT_NOK(OptionTypeInfo(0, OptionType::kBoolean)
3683                  .Parse(config_options, "b", "x", &b));
3684   ASSERT_NOK(
3685       OptionTypeInfo(0, OptionType::kInt).Parse(config_options, "b", "x", &i));
3686   ASSERT_NOK(OptionTypeInfo(0, OptionType::kInt32T)
3687                  .Parse(config_options, "b", "x", &i32));
3688   ASSERT_NOK(OptionTypeInfo(0, OptionType::kInt64T)
3689                  .Parse(config_options, "b", "x", &i64));
3690   ASSERT_NOK(
3691       OptionTypeInfo(0, OptionType::kUInt).Parse(config_options, "b", "x", &u));
3692   ASSERT_NOK(OptionTypeInfo(0, OptionType::kUInt32T)
3693                  .Parse(config_options, "b", "x", &u32));
3694   ASSERT_NOK(OptionTypeInfo(0, OptionType::kUInt64T)
3695                  .Parse(config_options, "b", "x", &u64));
3696   ASSERT_NOK(OptionTypeInfo(0, OptionType::kSizeT)
3697                  .Parse(config_options, "b", "x", &sz));
3698   ASSERT_NOK(OptionTypeInfo(0, OptionType::kDouble)
3699                  .Parse(config_options, "b", "x", &d));
3700 
3701   // Don't know how to convert Unknowns to anything else
3702   ASSERT_NOK(OptionTypeInfo(0, OptionType::kUnknown)
3703                  .Parse(config_options, "b", "x", &d));
3704 
3705   // Verify that if the parse function throws an exception, it is also trapped
3706   OptionTypeInfo func_info(0, OptionType::kUnknown,
3707                            OptionVerificationType::kNormal,
3708                            OptionTypeFlags::kNone,
3709                            [](const ConfigOptions&, const std::string&,
3710                               const std::string& value, void* addr) {
3711                              auto ptr = static_cast<int*>(addr);
3712                              *ptr = ParseInt(value);
3713                              return Status::OK();
3714                            });
3715   ASSERT_OK(func_info.Parse(config_options, "b", "1", &i));
3716   ASSERT_NOK(func_info.Parse(config_options, "b", "x", &i));
3717 }
3718 
TEST_F(OptionTypeInfoTest,TestParseFunc)3719 TEST_F(OptionTypeInfoTest, TestParseFunc) {
3720   OptionTypeInfo opt_info(
3721       0, OptionType::kUnknown, OptionVerificationType::kNormal,
3722       OptionTypeFlags::kNone,
3723       [](const ConfigOptions& /*opts*/, const std::string& name,
3724          const std::string& value, void* addr) {
3725         auto ptr = static_cast<std::string*>(addr);
3726         if (name == "Oops") {
3727           return Status::InvalidArgument(value);
3728         } else {
3729           *ptr = value + " " + name;
3730           return Status::OK();
3731         }
3732       });
3733   ConfigOptions config_options;
3734   std::string base;
3735   ASSERT_OK(opt_info.Parse(config_options, "World", "Hello", &base));
3736   ASSERT_EQ(base, "Hello World");
3737   ASSERT_NOK(opt_info.Parse(config_options, "Oops", "Hello", &base));
3738 }
3739 
TEST_F(OptionTypeInfoTest,TestSerializeFunc)3740 TEST_F(OptionTypeInfoTest, TestSerializeFunc) {
3741   OptionTypeInfo opt_info(
3742       0, OptionType::kString, OptionVerificationType::kNormal,
3743       OptionTypeFlags::kNone, nullptr,
3744       [](const ConfigOptions& /*opts*/, const std::string& name,
3745          const void* /*addr*/, std::string* value) {
3746         if (name == "Oops") {
3747           return Status::InvalidArgument(name);
3748         } else {
3749           *value = name;
3750           return Status::OK();
3751         }
3752       },
3753       nullptr);
3754   ConfigOptions config_options;
3755   std::string base;
3756   std::string value;
3757   ASSERT_OK(opt_info.Serialize(config_options, "Hello", &base, &value));
3758   ASSERT_EQ(value, "Hello");
3759   ASSERT_NOK(opt_info.Serialize(config_options, "Oops", &base, &value));
3760 }
3761 
TEST_F(OptionTypeInfoTest,TestEqualsFunc)3762 TEST_F(OptionTypeInfoTest, TestEqualsFunc) {
3763   OptionTypeInfo opt_info(
3764       0, OptionType::kInt, OptionVerificationType::kNormal,
3765       OptionTypeFlags::kNone, nullptr, nullptr,
3766       [](const ConfigOptions& /*opts*/, const std::string& name,
3767          const void* addr1, const void* addr2, std::string* mismatch) {
3768         auto i1 = *(static_cast<const int*>(addr1));
3769         auto i2 = *(static_cast<const int*>(addr2));
3770         if (name == "LT") {
3771           return i1 < i2;
3772         } else if (name == "GT") {
3773           return i1 > i2;
3774         } else if (name == "EQ") {
3775           return i1 == i2;
3776         } else {
3777           *mismatch = name + "???";
3778           return false;
3779         }
3780       });
3781 
3782   ConfigOptions config_options;
3783   int int1 = 100;
3784   int int2 = 200;
3785   std::string mismatch;
3786   ASSERT_TRUE(opt_info.AreEqual(config_options, "LT", &int1, &int2, &mismatch));
3787   ASSERT_EQ(mismatch, "");
3788   ASSERT_FALSE(
3789       opt_info.AreEqual(config_options, "GT", &int1, &int2, &mismatch));
3790   ASSERT_EQ(mismatch, "GT");
3791   ASSERT_FALSE(
3792       opt_info.AreEqual(config_options, "NO", &int1, &int2, &mismatch));
3793   ASSERT_EQ(mismatch, "NO???");
3794 }
3795 
TEST_F(OptionTypeInfoTest,TestOptionFlags)3796 TEST_F(OptionTypeInfoTest, TestOptionFlags) {
3797   OptionTypeInfo opt_none(0, OptionType::kString,
3798                           OptionVerificationType::kNormal,
3799                           OptionTypeFlags::kDontSerialize);
3800   OptionTypeInfo opt_never(0, OptionType::kString,
3801                            OptionVerificationType::kNormal,
3802                            OptionTypeFlags::kCompareNever);
3803   OptionTypeInfo opt_alias(0, OptionType::kString,
3804                            OptionVerificationType::kAlias,
3805                            OptionTypeFlags::kNone);
3806   OptionTypeInfo opt_deprecated(0, OptionType::kString,
3807                                 OptionVerificationType::kDeprecated,
3808                                 OptionTypeFlags::kNone);
3809   ConfigOptions config_options;
3810   std::string opts_str;
3811   std::string base = "base";
3812   std::string comp = "comp";
3813 
3814   // If marked string none, the serialization returns not supported
3815   ASSERT_NOK(opt_none.Serialize(config_options, "None", &base, &opts_str));
3816   // If marked never compare, they match even when they do not
3817   ASSERT_TRUE(opt_never.AreEqual(config_options, "Never", &base, &comp, &base));
3818   ASSERT_FALSE(opt_none.AreEqual(config_options, "Never", &base, &comp, &base));
3819 
3820   // An alias can change the value via parse, but does nothing on serialize on
3821   // match
3822   std::string result;
3823   ASSERT_OK(opt_alias.Parse(config_options, "Alias", "Alias", &base));
3824   ASSERT_OK(opt_alias.Serialize(config_options, "Alias", &base, &result));
3825   ASSERT_TRUE(
3826       opt_alias.AreEqual(config_options, "Alias", &base, &comp, &result));
3827   ASSERT_EQ(base, "Alias");
3828   ASSERT_NE(base, comp);
3829 
3830   // Deprecated options do nothing on any of the commands
3831   ASSERT_OK(opt_deprecated.Parse(config_options, "Alias", "Deprecated", &base));
3832   ASSERT_OK(opt_deprecated.Serialize(config_options, "Alias", &base, &result));
3833   ASSERT_TRUE(
3834       opt_deprecated.AreEqual(config_options, "Alias", &base, &comp, &result));
3835   ASSERT_EQ(base, "Alias");
3836   ASSERT_NE(base, comp);
3837 }
3838 
TEST_F(OptionTypeInfoTest,TestCustomEnum)3839 TEST_F(OptionTypeInfoTest, TestCustomEnum) {
3840   enum TestEnum { kA, kB, kC };
3841   std::unordered_map<std::string, TestEnum> enum_map = {
3842       {"A", TestEnum::kA},
3843       {"B", TestEnum::kB},
3844       {"C", TestEnum::kC},
3845   };
3846   OptionTypeInfo opt_info = OptionTypeInfo::Enum<TestEnum>(0, &enum_map);
3847   TestEnum e1, e2;
3848   ConfigOptions config_options;
3849   std::string result, mismatch;
3850 
3851   e2 = TestEnum::kA;
3852 
3853   ASSERT_OK(opt_info.Parse(config_options, "", "B", &e1));
3854   ASSERT_OK(opt_info.Serialize(config_options, "", &e1, &result));
3855   ASSERT_EQ(e1, TestEnum::kB);
3856   ASSERT_EQ(result, "B");
3857 
3858   ASSERT_FALSE(opt_info.AreEqual(config_options, "Enum", &e1, &e2, &mismatch));
3859   ASSERT_EQ(mismatch, "Enum");
3860 
3861   TestAndCompareOption(config_options, opt_info, "", "C", &e1, &e2);
3862   ASSERT_EQ(e2, TestEnum::kC);
3863 
3864   ASSERT_NOK(opt_info.Parse(config_options, "", "D", &e1));
3865   ASSERT_EQ(e1, TestEnum::kC);
3866 }
3867 
TEST_F(OptionTypeInfoTest,TestBuiltinEnum)3868 TEST_F(OptionTypeInfoTest, TestBuiltinEnum) {
3869   ConfigOptions config_options;
3870   for (auto iter : OptionsHelper::compaction_style_string_map) {
3871     CompactionStyle e1, e2;
3872     TestAndCompareOption(config_options,
3873                          OptionTypeInfo(0, OptionType::kCompactionStyle),
3874                          "CompactionStyle", iter.first, &e1, &e2);
3875     ASSERT_EQ(e1, iter.second);
3876   }
3877   for (auto iter : OptionsHelper::compaction_pri_string_map) {
3878     CompactionPri e1, e2;
3879     TestAndCompareOption(config_options,
3880                          OptionTypeInfo(0, OptionType::kCompactionPri),
3881                          "CompactionPri", iter.first, &e1, &e2);
3882     ASSERT_EQ(e1, iter.second);
3883   }
3884   for (auto iter : OptionsHelper::compression_type_string_map) {
3885     CompressionType e1, e2;
3886     TestAndCompareOption(config_options,
3887                          OptionTypeInfo(0, OptionType::kCompressionType),
3888                          "CompressionType", iter.first, &e1, &e2);
3889     ASSERT_EQ(e1, iter.second);
3890   }
3891   for (auto iter : OptionsHelper::compaction_stop_style_string_map) {
3892     CompactionStopStyle e1, e2;
3893     TestAndCompareOption(config_options,
3894                          OptionTypeInfo(0, OptionType::kCompactionStopStyle),
3895                          "CompactionStopStyle", iter.first, &e1, &e2);
3896     ASSERT_EQ(e1, iter.second);
3897   }
3898   for (auto iter : OptionsHelper::checksum_type_string_map) {
3899     ChecksumType e1, e2;
3900     TestAndCompareOption(config_options,
3901                          OptionTypeInfo(0, OptionType::kChecksumType),
3902                          "CheckSumType", iter.first, &e1, &e2);
3903     ASSERT_EQ(e1, iter.second);
3904   }
3905   for (auto iter : OptionsHelper::encoding_type_string_map) {
3906     EncodingType e1, e2;
3907     TestAndCompareOption(config_options,
3908                          OptionTypeInfo(0, OptionType::kEncodingType),
3909                          "EncodingType", iter.first, &e1, &e2);
3910     ASSERT_EQ(e1, iter.second);
3911   }
3912 }
3913 
TEST_F(OptionTypeInfoTest,TestStruct)3914 TEST_F(OptionTypeInfoTest, TestStruct) {
3915   struct Basic {
3916     int i = 42;
3917     std::string s = "Hello";
3918   };
3919 
3920   struct Extended {
3921     int j = 11;
3922     Basic b;
3923   };
3924 
3925   std::unordered_map<std::string, OptionTypeInfo> basic_type_map = {
3926       {"i", {offsetof(struct Basic, i), OptionType::kInt}},
3927       {"s", {offsetof(struct Basic, s), OptionType::kString}},
3928   };
3929   OptionTypeInfo basic_info = OptionTypeInfo::Struct(
3930       "b", &basic_type_map, 0, OptionVerificationType::kNormal,
3931       OptionTypeFlags::kMutable);
3932 
3933   std::unordered_map<std::string, OptionTypeInfo> extended_type_map = {
3934       {"j", {offsetof(struct Extended, j), OptionType::kInt}},
3935       {"b", OptionTypeInfo::Struct(
3936                 "b", &basic_type_map, offsetof(struct Extended, b),
3937                 OptionVerificationType::kNormal, OptionTypeFlags::kNone)},
3938       {"m", OptionTypeInfo::Struct(
3939                 "m", &basic_type_map, offsetof(struct Extended, b),
3940                 OptionVerificationType::kNormal, OptionTypeFlags::kMutable)},
3941   };
3942   OptionTypeInfo extended_info = OptionTypeInfo::Struct(
3943       "e", &extended_type_map, 0, OptionVerificationType::kNormal,
3944       OptionTypeFlags::kMutable);
3945   Extended e1, e2;
3946   ConfigOptions config_options;
3947   std::string mismatch;
3948   TestAndCompareOption(config_options, basic_info, "b", "{i=33;s=33}", &e1.b,
3949                        &e2.b);
3950   ASSERT_EQ(e1.b.i, 33);
3951   ASSERT_EQ(e1.b.s, "33");
3952 
3953   TestAndCompareOption(config_options, basic_info, "b.i", "44", &e1.b, &e2.b);
3954   ASSERT_EQ(e1.b.i, 44);
3955 
3956   TestAndCompareOption(config_options, basic_info, "i", "55", &e1.b, &e2.b);
3957   ASSERT_EQ(e1.b.i, 55);
3958 
3959   e1.b.i = 0;
3960 
3961   ASSERT_FALSE(
3962       basic_info.AreEqual(config_options, "b", &e1.b, &e2.b, &mismatch));
3963   ASSERT_EQ(mismatch, "b.i");
3964   mismatch.clear();
3965   ASSERT_FALSE(
3966       basic_info.AreEqual(config_options, "b.i", &e1.b, &e2.b, &mismatch));
3967   ASSERT_EQ(mismatch, "b.i");
3968   mismatch.clear();
3969   ASSERT_FALSE(
3970       basic_info.AreEqual(config_options, "i", &e1.b, &e2.b, &mismatch));
3971   ASSERT_EQ(mismatch, "b.i");
3972   mismatch.clear();
3973 
3974   e1 = e2;
3975   ASSERT_NOK(basic_info.Parse(config_options, "b", "{i=33;s=33;j=44}", &e1.b));
3976   ASSERT_NOK(basic_info.Parse(config_options, "b.j", "44", &e1.b));
3977   ASSERT_NOK(basic_info.Parse(config_options, "j", "44", &e1.b));
3978 
3979   TestAndCompareOption(config_options, extended_info, "e",
3980                        "b={i=55;s=55}; j=22;", &e1, &e2);
3981   ASSERT_EQ(e1.b.i, 55);
3982   ASSERT_EQ(e1.j, 22);
3983   ASSERT_EQ(e1.b.s, "55");
3984   TestAndCompareOption(config_options, extended_info, "e.b", "{i=66;s=66;}",
3985                        &e1, &e2);
3986   ASSERT_EQ(e1.b.i, 66);
3987   ASSERT_EQ(e1.j, 22);
3988   ASSERT_EQ(e1.b.s, "66");
3989   TestAndCompareOption(config_options, extended_info, "e.b.i", "77", &e1, &e2);
3990   ASSERT_EQ(e1.b.i, 77);
3991   ASSERT_EQ(e1.j, 22);
3992   ASSERT_EQ(e1.b.s, "66");
3993 }
3994 
TEST_F(OptionTypeInfoTest,TestVectorType)3995 TEST_F(OptionTypeInfoTest, TestVectorType) {
3996   OptionTypeInfo vec_info = OptionTypeInfo::Vector<std::string>(
3997       0, OptionVerificationType::kNormal, OptionTypeFlags::kNone,
3998       {0, OptionType::kString});
3999   std::vector<std::string> vec1, vec2;
4000   std::string mismatch;
4001 
4002   ConfigOptions config_options;
4003   TestAndCompareOption(config_options, vec_info, "v", "a:b:c:d", &vec1, &vec2);
4004   ASSERT_EQ(vec1.size(), 4);
4005   ASSERT_EQ(vec1[0], "a");
4006   ASSERT_EQ(vec1[1], "b");
4007   ASSERT_EQ(vec1[2], "c");
4008   ASSERT_EQ(vec1[3], "d");
4009   vec1[3] = "e";
4010   ASSERT_FALSE(vec_info.AreEqual(config_options, "v", &vec1, &vec2, &mismatch));
4011   ASSERT_EQ(mismatch, "v");
4012 
4013   // Test vectors with inner brackets
4014   TestAndCompareOption(config_options, vec_info, "v", "a:{b}:c:d", &vec1,
4015                        &vec2);
4016   ASSERT_EQ(vec1.size(), 4);
4017   ASSERT_EQ(vec1[0], "a");
4018   ASSERT_EQ(vec1[1], "b");
4019   ASSERT_EQ(vec1[2], "c");
4020   ASSERT_EQ(vec1[3], "d");
4021 
4022   OptionTypeInfo bar_info = OptionTypeInfo::Vector<std::string>(
4023       0, OptionVerificationType::kNormal, OptionTypeFlags::kNone,
4024       {0, OptionType::kString}, '|');
4025   TestAndCompareOption(config_options, vec_info, "v", "x|y|z", &vec1, &vec2);
4026   // Test vectors with inner vector
4027   TestAndCompareOption(config_options, bar_info, "v",
4028                        "a|{b1|b2}|{c1|c2|{d1|d2}}", &vec1, &vec2);
4029   ASSERT_EQ(vec1.size(), 3);
4030   ASSERT_EQ(vec1[0], "a");
4031   ASSERT_EQ(vec1[1], "b1|b2");
4032   ASSERT_EQ(vec1[2], "c1|c2|{d1|d2}");
4033 }
4034 
TEST_F(OptionTypeInfoTest,TestStaticType)4035 TEST_F(OptionTypeInfoTest, TestStaticType) {
4036   struct SimpleOptions {
4037     size_t size = 0;
4038     bool verify = true;
4039   };
4040 
4041   static std::unordered_map<std::string, OptionTypeInfo> type_map = {
4042       {"size", {offsetof(struct SimpleOptions, size), OptionType::kSizeT}},
4043       {"verify",
4044        {offsetof(struct SimpleOptions, verify), OptionType::kBoolean}},
4045   };
4046 
4047   ConfigOptions config_options;
4048   SimpleOptions opts, copy;
4049   opts.size = 12345;
4050   opts.verify = false;
4051   std::string str, mismatch;
4052 
4053   ASSERT_OK(
4054       OptionTypeInfo::SerializeType(config_options, type_map, &opts, &str));
4055   ASSERT_FALSE(OptionTypeInfo::TypesAreEqual(config_options, type_map, &opts,
4056                                              &copy, &mismatch));
4057   ASSERT_OK(OptionTypeInfo::ParseType(config_options, str, type_map, &copy));
4058   ASSERT_TRUE(OptionTypeInfo::TypesAreEqual(config_options, type_map, &opts,
4059                                             &copy, &mismatch));
4060 }
4061 
4062 class ConfigOptionsTest : public testing::Test {};
4063 
TEST_F(ConfigOptionsTest,EnvFromConfigOptions)4064 TEST_F(ConfigOptionsTest, EnvFromConfigOptions) {
4065   ConfigOptions config_options;
4066   DBOptions db_opts;
4067   Options opts;
4068   Env* mem_env = NewMemEnv(Env::Default());
4069   config_options.registry->AddLibrary("custom-env", RegisterCustomEnv,
4070                                       kCustomEnvName);
4071 
4072   config_options.env = mem_env;
4073   // First test that we can get the env as expected
4074   ASSERT_OK(GetDBOptionsFromString(config_options, DBOptions(), kCustomEnvProp,
4075                                    &db_opts));
4076   ASSERT_OK(
4077       GetOptionsFromString(config_options, Options(), kCustomEnvProp, &opts));
4078   ASSERT_NE(config_options.env, db_opts.env);
4079   ASSERT_EQ(opts.env, db_opts.env);
4080   Env* custom_env = db_opts.env;
4081 
4082   // Now try a "bad" env" and check that nothing changed
4083   config_options.ignore_unsupported_options = true;
4084   ASSERT_OK(
4085       GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
4086   ASSERT_OK(GetOptionsFromString(config_options, opts, "env=unknown", &opts));
4087   ASSERT_EQ(config_options.env, mem_env);
4088   ASSERT_EQ(db_opts.env, custom_env);
4089   ASSERT_EQ(opts.env, db_opts.env);
4090 
4091   // Now try a "bad" env" ignoring unknown objects
4092   config_options.ignore_unsupported_options = false;
4093   ASSERT_NOK(
4094       GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
4095   ASSERT_EQ(config_options.env, mem_env);
4096   ASSERT_EQ(db_opts.env, custom_env);
4097   ASSERT_EQ(opts.env, db_opts.env);
4098 
4099   delete mem_env;
4100 }
4101 #endif  // !ROCKSDB_LITE
4102 }  // namespace ROCKSDB_NAMESPACE
4103 
main(int argc,char ** argv)4104 int main(int argc, char** argv) {
4105   ::testing::InitGoogleTest(&argc, argv);
4106 #ifdef GFLAGS
4107   ParseCommandLineFlags(&argc, &argv, true);
4108 #endif  // GFLAGS
4109   return RUN_ALL_TESTS();
4110 }
4111