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 #ifndef ROCKSDB_LITE
7
8 #include "rocksdb/utilities/options_util.h"
9
10 #include <cctype>
11 #include <cinttypes>
12 #include <unordered_map>
13
14 #include "env/mock_env.h"
15 #include "file/filename.h"
16 #include "options/options_parser.h"
17 #include "rocksdb/convenience.h"
18 #include "rocksdb/db.h"
19 #include "rocksdb/table.h"
20 #include "test_util/testharness.h"
21 #include "test_util/testutil.h"
22 #include "util/random.h"
23
24 #ifndef GFLAGS
25 bool FLAGS_enable_print = false;
26 #else
27 #include "util/gflags_compat.h"
28 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
29 DEFINE_bool(enable_print, false, "Print options generated to console.");
30 #endif // GFLAGS
31
32 namespace ROCKSDB_NAMESPACE {
33 class OptionsUtilTest : public testing::Test {
34 public:
OptionsUtilTest()35 OptionsUtilTest() : rnd_(0xFB) {
36 env_.reset(NewMemEnv(Env::Default()));
37 dbname_ = test::PerThreadDBPath("options_util_test");
38 }
39
40 protected:
41 std::unique_ptr<Env> env_;
42 std::string dbname_;
43 Random rnd_;
44 };
45
TEST_F(OptionsUtilTest,SaveAndLoad)46 TEST_F(OptionsUtilTest, SaveAndLoad) {
47 const size_t kCFCount = 5;
48
49 DBOptions db_opt;
50 std::vector<std::string> cf_names;
51 std::vector<ColumnFamilyOptions> cf_opts;
52 test::RandomInitDBOptions(&db_opt, &rnd_);
53 for (size_t i = 0; i < kCFCount; ++i) {
54 cf_names.push_back(i == 0 ? kDefaultColumnFamilyName
55 : test::RandomName(&rnd_, 10));
56 cf_opts.emplace_back();
57 test::RandomInitCFOptions(&cf_opts.back(), db_opt, &rnd_);
58 }
59
60 const std::string kFileName = "OPTIONS-123456";
61 ASSERT_OK(PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName,
62 env_->GetFileSystem().get()));
63
64 DBOptions loaded_db_opt;
65 std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
66 ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt,
67 &loaded_cf_descs));
68 ConfigOptions exact;
69 exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
70 ASSERT_OK(
71 RocksDBOptionsParser::VerifyDBOptions(exact, db_opt, loaded_db_opt));
72 test::RandomInitDBOptions(&db_opt, &rnd_);
73 ASSERT_NOK(
74 RocksDBOptionsParser::VerifyDBOptions(exact, db_opt, loaded_db_opt));
75
76 for (size_t i = 0; i < kCFCount; ++i) {
77 ASSERT_EQ(cf_names[i], loaded_cf_descs[i].name);
78 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
79 exact, cf_opts[i], loaded_cf_descs[i].options));
80 ASSERT_OK(RocksDBOptionsParser::VerifyTableFactory(
81 exact, cf_opts[i].table_factory.get(),
82 loaded_cf_descs[i].options.table_factory.get()));
83 test::RandomInitCFOptions(&cf_opts[i], db_opt, &rnd_);
84 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
85 exact, cf_opts[i], loaded_cf_descs[i].options));
86 }
87
88 ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_opts[0])));
89 for (size_t i = 0; i < kCFCount; ++i) {
90 if (cf_opts[i].compaction_filter) {
91 delete cf_opts[i].compaction_filter;
92 }
93 }
94 }
95
TEST_F(OptionsUtilTest,SaveAndLoadWithCacheCheck)96 TEST_F(OptionsUtilTest, SaveAndLoadWithCacheCheck) {
97 // creating db
98 DBOptions db_opt;
99 db_opt.create_if_missing = true;
100 // initialize BlockBasedTableOptions
101 std::shared_ptr<Cache> cache = NewLRUCache(1 * 1024);
102 BlockBasedTableOptions bbt_opts;
103 bbt_opts.block_size = 32 * 1024;
104 // saving cf options
105 std::vector<ColumnFamilyOptions> cf_opts;
106 ColumnFamilyOptions default_column_family_opt = ColumnFamilyOptions();
107 default_column_family_opt.table_factory.reset(
108 NewBlockBasedTableFactory(bbt_opts));
109 cf_opts.push_back(default_column_family_opt);
110
111 ColumnFamilyOptions cf_opt_sample = ColumnFamilyOptions();
112 cf_opt_sample.table_factory.reset(NewBlockBasedTableFactory(bbt_opts));
113 cf_opts.push_back(cf_opt_sample);
114
115 ColumnFamilyOptions cf_opt_plain_table_opt = ColumnFamilyOptions();
116 cf_opt_plain_table_opt.table_factory.reset(NewPlainTableFactory());
117 cf_opts.push_back(cf_opt_plain_table_opt);
118
119 std::vector<std::string> cf_names;
120 cf_names.push_back(kDefaultColumnFamilyName);
121 cf_names.push_back("cf_sample");
122 cf_names.push_back("cf_plain_table_sample");
123 // Saving DB in file
124 const std::string kFileName = "OPTIONS-LOAD_CACHE_123456";
125 ASSERT_OK(PersistRocksDBOptions(db_opt, cf_names, cf_opts, kFileName,
126 env_->GetFileSystem().get()));
127 DBOptions loaded_db_opt;
128 std::vector<ColumnFamilyDescriptor> loaded_cf_descs;
129
130 ConfigOptions config_options;
131 config_options.ignore_unknown_options = false;
132 config_options.input_strings_escaped = true;
133 config_options.env = env_.get();
134 ASSERT_OK(LoadOptionsFromFile(config_options, kFileName, &loaded_db_opt,
135 &loaded_cf_descs, &cache));
136 for (size_t i = 0; i < loaded_cf_descs.size(); i++) {
137 auto* loaded_bbt_opt =
138 loaded_cf_descs[i]
139 .options.table_factory->GetOptions<BlockBasedTableOptions>();
140 // Expect the same cache will be loaded
141 if (loaded_bbt_opt != nullptr) {
142 ASSERT_EQ(loaded_bbt_opt->block_cache.get(), cache.get());
143 }
144 }
145
146 // Test the old interface
147 ASSERT_OK(LoadOptionsFromFile(kFileName, env_.get(), &loaded_db_opt,
148 &loaded_cf_descs, false, &cache));
149 for (size_t i = 0; i < loaded_cf_descs.size(); i++) {
150 auto* loaded_bbt_opt =
151 loaded_cf_descs[i]
152 .options.table_factory->GetOptions<BlockBasedTableOptions>();
153 // Expect the same cache will be loaded
154 if (loaded_bbt_opt != nullptr) {
155 ASSERT_EQ(loaded_bbt_opt->block_cache.get(), cache.get());
156 }
157 }
158 ASSERT_OK(DestroyDB(dbname_, Options(loaded_db_opt, cf_opts[0])));
159 }
160
161 namespace {
162 class DummyTableFactory : public TableFactory {
163 public:
DummyTableFactory()164 DummyTableFactory() {}
~DummyTableFactory()165 ~DummyTableFactory() override {}
166
Name() const167 const char* Name() const override { return "DummyTableFactory"; }
168
169 using TableFactory::NewTableReader;
NewTableReader(const ReadOptions &,const TableReaderOptions &,std::unique_ptr<RandomAccessFileReader> &&,uint64_t,std::unique_ptr<TableReader> *,bool) const170 Status NewTableReader(
171 const ReadOptions& /*ro*/,
172 const TableReaderOptions& /*table_reader_options*/,
173 std::unique_ptr<RandomAccessFileReader>&& /*file*/,
174 uint64_t /*file_size*/, std::unique_ptr<TableReader>* /*table_reader*/,
175 bool /*prefetch_index_and_filter_in_cache*/) const override {
176 return Status::NotSupported();
177 }
178
NewTableBuilder(const TableBuilderOptions &,WritableFileWriter *) const179 TableBuilder* NewTableBuilder(
180 const TableBuilderOptions& /*table_builder_options*/,
181 WritableFileWriter* /*file*/) const override {
182 return nullptr;
183 }
184
ValidateOptions(const DBOptions &,const ColumnFamilyOptions &) const185 Status ValidateOptions(
186 const DBOptions& /*db_opts*/,
187 const ColumnFamilyOptions& /*cf_opts*/) const override {
188 return Status::NotSupported();
189 }
190
GetPrintableOptions() const191 std::string GetPrintableOptions() const override { return ""; }
192 };
193
194 class DummyMergeOperator : public MergeOperator {
195 public:
DummyMergeOperator()196 DummyMergeOperator() {}
~DummyMergeOperator()197 ~DummyMergeOperator() override {}
198
FullMergeV2(const MergeOperationInput &,MergeOperationOutput *) const199 bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
200 MergeOperationOutput* /*merge_out*/) const override {
201 return false;
202 }
203
PartialMergeMulti(const Slice &,const std::deque<Slice> &,std::string *,Logger *) const204 bool PartialMergeMulti(const Slice& /*key*/,
205 const std::deque<Slice>& /*operand_list*/,
206 std::string* /*new_value*/,
207 Logger* /*logger*/) const override {
208 return false;
209 }
210
Name() const211 const char* Name() const override { return "DummyMergeOperator"; }
212 };
213
214 class DummySliceTransform : public SliceTransform {
215 public:
DummySliceTransform()216 DummySliceTransform() {}
~DummySliceTransform()217 ~DummySliceTransform() override {}
218
219 // Return the name of this transformation.
Name() const220 const char* Name() const override { return "DummySliceTransform"; }
221
222 // transform a src in domain to a dst in the range
Transform(const Slice & src) const223 Slice Transform(const Slice& src) const override { return src; }
224
225 // determine whether this is a valid src upon the function applies
InDomain(const Slice &) const226 bool InDomain(const Slice& /*src*/) const override { return false; }
227
228 // determine whether dst=Transform(src) for some src
InRange(const Slice &) const229 bool InRange(const Slice& /*dst*/) const override { return false; }
230 };
231
232 } // namespace
233
TEST_F(OptionsUtilTest,SanityCheck)234 TEST_F(OptionsUtilTest, SanityCheck) {
235 DBOptions db_opt;
236 std::vector<ColumnFamilyDescriptor> cf_descs;
237 const size_t kCFCount = 5;
238 for (size_t i = 0; i < kCFCount; ++i) {
239 cf_descs.emplace_back();
240 cf_descs.back().name =
241 (i == 0) ? kDefaultColumnFamilyName : test::RandomName(&rnd_, 10);
242
243 cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());
244 // Assign non-null values to prefix_extractors except the first cf.
245 cf_descs.back().options.prefix_extractor.reset(
246 i != 0 ? test::RandomSliceTransform(&rnd_) : nullptr);
247 cf_descs.back().options.merge_operator.reset(
248 test::RandomMergeOperator(&rnd_));
249 }
250
251 db_opt.create_missing_column_families = true;
252 db_opt.create_if_missing = true;
253
254 ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)));
255 DB* db;
256 std::vector<ColumnFamilyHandle*> handles;
257 // open and persist the options
258 ASSERT_OK(DB::Open(db_opt, dbname_, cf_descs, &handles, &db));
259
260 // close the db
261 for (auto* handle : handles) {
262 delete handle;
263 }
264 delete db;
265
266 ConfigOptions config_options;
267 config_options.ignore_unknown_options = false;
268 config_options.input_strings_escaped = true;
269 config_options.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
270 // perform sanity check
271 ASSERT_OK(
272 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
273
274 ASSERT_GE(kCFCount, 5);
275 // merge operator
276 {
277 std::shared_ptr<MergeOperator> merge_op =
278 cf_descs[0].options.merge_operator;
279
280 ASSERT_NE(merge_op.get(), nullptr);
281 cf_descs[0].options.merge_operator.reset();
282 ASSERT_NOK(
283 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
284
285 cf_descs[0].options.merge_operator.reset(new DummyMergeOperator());
286 ASSERT_NOK(
287 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
288
289 cf_descs[0].options.merge_operator = merge_op;
290 ASSERT_OK(
291 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
292 }
293
294 // prefix extractor
295 {
296 std::shared_ptr<const SliceTransform> prefix_extractor =
297 cf_descs[1].options.prefix_extractor;
298
299 // It's okay to set prefix_extractor to nullptr.
300 ASSERT_NE(prefix_extractor, nullptr);
301 cf_descs[1].options.prefix_extractor.reset();
302 ASSERT_OK(
303 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
304
305 cf_descs[1].options.prefix_extractor.reset(new DummySliceTransform());
306 ASSERT_OK(
307 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
308
309 cf_descs[1].options.prefix_extractor = prefix_extractor;
310 ASSERT_OK(
311 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
312 }
313
314 // prefix extractor nullptr case
315 {
316 std::shared_ptr<const SliceTransform> prefix_extractor =
317 cf_descs[0].options.prefix_extractor;
318
319 // It's okay to set prefix_extractor to nullptr.
320 ASSERT_EQ(prefix_extractor, nullptr);
321 cf_descs[0].options.prefix_extractor.reset();
322 ASSERT_OK(
323 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
324
325 // It's okay to change prefix_extractor from nullptr to non-nullptr
326 cf_descs[0].options.prefix_extractor.reset(new DummySliceTransform());
327 ASSERT_OK(
328 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
329
330 cf_descs[0].options.prefix_extractor = prefix_extractor;
331 ASSERT_OK(
332 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
333 }
334
335 // comparator
336 {
337 test::SimpleSuffixReverseComparator comparator;
338
339 auto* prev_comparator = cf_descs[2].options.comparator;
340 cf_descs[2].options.comparator = &comparator;
341 ASSERT_NOK(
342 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
343
344 cf_descs[2].options.comparator = prev_comparator;
345 ASSERT_OK(
346 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
347 }
348
349 // table factory
350 {
351 std::shared_ptr<TableFactory> table_factory =
352 cf_descs[3].options.table_factory;
353
354 ASSERT_NE(table_factory, nullptr);
355 cf_descs[3].options.table_factory.reset(new DummyTableFactory());
356 ASSERT_NOK(
357 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
358
359 cf_descs[3].options.table_factory = table_factory;
360 ASSERT_OK(
361 CheckOptionsCompatibility(config_options, dbname_, db_opt, cf_descs));
362 }
363 ASSERT_OK(DestroyDB(dbname_, Options(db_opt, cf_descs[0].options)));
364 }
365
TEST_F(OptionsUtilTest,LatestOptionsNotFound)366 TEST_F(OptionsUtilTest, LatestOptionsNotFound) {
367 std::unique_ptr<Env> env(NewMemEnv(Env::Default()));
368 Status s;
369 Options options;
370 ConfigOptions config_opts;
371 std::vector<ColumnFamilyDescriptor> cf_descs;
372
373 options.env = env.get();
374 options.create_if_missing = true;
375 config_opts.env = options.env;
376 config_opts.ignore_unknown_options = false;
377
378 std::vector<std::string> children;
379
380 std::string options_file_name;
381 ASSERT_OK(DestroyDB(dbname_, options));
382 // First, test where the db directory does not exist
383 ASSERT_NOK(options.env->GetChildren(dbname_, &children));
384
385 s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
386 ASSERT_TRUE(s.IsNotFound());
387 ASSERT_TRUE(s.IsPathNotFound());
388
389 s = LoadLatestOptions(dbname_, options.env, &options, &cf_descs);
390 ASSERT_TRUE(s.IsNotFound());
391 ASSERT_TRUE(s.IsPathNotFound());
392
393 s = LoadLatestOptions(config_opts, dbname_, &options, &cf_descs);
394 ASSERT_TRUE(s.IsPathNotFound());
395
396 s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
397 ASSERT_TRUE(s.IsNotFound());
398 ASSERT_TRUE(s.IsPathNotFound());
399
400 // Second, test where the db directory exists but is empty
401 ASSERT_OK(options.env->CreateDir(dbname_));
402
403 s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
404 ASSERT_TRUE(s.IsNotFound());
405 ASSERT_TRUE(s.IsPathNotFound());
406
407 s = LoadLatestOptions(dbname_, options.env, &options, &cf_descs);
408 ASSERT_TRUE(s.IsNotFound());
409 ASSERT_TRUE(s.IsPathNotFound());
410
411 // Finally, test where a file exists but is not an "Options" file
412 std::unique_ptr<WritableFile> file;
413 ASSERT_OK(
414 options.env->NewWritableFile(dbname_ + "/temp.txt", &file, EnvOptions()));
415 ASSERT_OK(file->Close());
416 s = GetLatestOptionsFileName(dbname_, options.env, &options_file_name);
417 ASSERT_TRUE(s.IsNotFound());
418 ASSERT_TRUE(s.IsPathNotFound());
419
420 s = LoadLatestOptions(config_opts, dbname_, &options, &cf_descs);
421 ASSERT_TRUE(s.IsNotFound());
422 ASSERT_TRUE(s.IsPathNotFound());
423 ASSERT_OK(options.env->DeleteFile(dbname_ + "/temp.txt"));
424 ASSERT_OK(options.env->DeleteDir(dbname_));
425 }
426
TEST_F(OptionsUtilTest,LoadLatestOptions)427 TEST_F(OptionsUtilTest, LoadLatestOptions) {
428 Options options;
429 options.OptimizeForSmallDb();
430 ColumnFamilyDescriptor cf_desc;
431 ConfigOptions config_opts;
432 DBOptions db_opts;
433 std::vector<ColumnFamilyDescriptor> cf_descs;
434 std::vector<ColumnFamilyHandle*> handles;
435 DB* db;
436 options.create_if_missing = true;
437
438 ASSERT_OK(DestroyDB(dbname_, options));
439
440 cf_descs.emplace_back();
441 cf_descs.back().name = kDefaultColumnFamilyName;
442 cf_descs.back().options.table_factory.reset(NewBlockBasedTableFactory());
443 cf_descs.emplace_back();
444 cf_descs.back().name = "Plain";
445 cf_descs.back().options.table_factory.reset(NewPlainTableFactory());
446 db_opts.create_missing_column_families = true;
447 db_opts.create_if_missing = true;
448
449 // open and persist the options
450 ASSERT_OK(DB::Open(db_opts, dbname_, cf_descs, &handles, &db));
451
452 std::string options_file_name;
453 std::string new_options_file;
454
455 ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &options_file_name));
456 ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
457 ASSERT_EQ(cf_descs.size(), 2U);
458 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
459 db->GetDBOptions(), db_opts));
460 ASSERT_OK(handles[0]->GetDescriptor(&cf_desc));
461 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
462 cf_descs[0].options));
463 ASSERT_OK(handles[1]->GetDescriptor(&cf_desc));
464 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
465 cf_descs[1].options));
466
467 // Now change some of the DBOptions
468 ASSERT_OK(db->SetDBOptions(
469 {{"delayed_write_rate", "1234"}, {"bytes_per_sync", "32768"}}));
470 ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &new_options_file));
471 ASSERT_NE(options_file_name, new_options_file);
472 ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
473 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
474 db->GetDBOptions(), db_opts));
475 options_file_name = new_options_file;
476
477 // Now change some of the ColumnFamilyOptions
478 ASSERT_OK(db->SetOptions(handles[1], {{"write_buffer_size", "32768"}}));
479 ASSERT_OK(GetLatestOptionsFileName(dbname_, options.env, &new_options_file));
480 ASSERT_NE(options_file_name, new_options_file);
481 ASSERT_OK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
482 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_opts,
483 db->GetDBOptions(), db_opts));
484 ASSERT_OK(handles[0]->GetDescriptor(&cf_desc));
485 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
486 cf_descs[0].options));
487 ASSERT_OK(handles[1]->GetDescriptor(&cf_desc));
488 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_opts, cf_desc.options,
489 cf_descs[1].options));
490
491 // close the db
492 for (auto* handle : handles) {
493 delete handle;
494 }
495 delete db;
496 ASSERT_OK(DestroyDB(dbname_, options, cf_descs));
497 }
498
WriteOptionsFile(Env * env,const std::string & path,const std::string & options_file,int major,int minor,const std::string & db_opts,const std::string & cf_opts,const std::string & bbt_opts="")499 static void WriteOptionsFile(Env* env, const std::string& path,
500 const std::string& options_file, int major,
501 int minor, const std::string& db_opts,
502 const std::string& cf_opts,
503 const std::string& bbt_opts = "") {
504 std::string options_file_header =
505 "\n"
506 "[Version]\n"
507 " rocksdb_version=" +
508 ToString(major) + "." + ToString(minor) +
509 ".0\n"
510 " options_file_version=1\n";
511
512 std::unique_ptr<WritableFile> wf;
513 ASSERT_OK(env->NewWritableFile(path + "/" + options_file, &wf, EnvOptions()));
514 ASSERT_OK(
515 wf->Append(options_file_header + "[ DBOptions ]\n" + db_opts + "\n"));
516 ASSERT_OK(wf->Append(
517 "[CFOptions \"default\"] # column family must be specified\n" +
518 cf_opts + "\n"));
519 ASSERT_OK(wf->Append("[TableOptions/BlockBasedTable \"default\"]\n" +
520 bbt_opts + "\n"));
521 ASSERT_OK(wf->Close());
522
523 std::string latest_options_file;
524 ASSERT_OK(GetLatestOptionsFileName(path, env, &latest_options_file));
525 ASSERT_EQ(latest_options_file, options_file);
526 }
527
TEST_F(OptionsUtilTest,BadLatestOptions)528 TEST_F(OptionsUtilTest, BadLatestOptions) {
529 Status s;
530 ConfigOptions config_opts;
531 DBOptions db_opts;
532 std::vector<ColumnFamilyDescriptor> cf_descs;
533 Options options;
534 options.env = env_.get();
535 config_opts.env = env_.get();
536 config_opts.ignore_unknown_options = false;
537 config_opts.delimiter = "\n";
538
539 ConfigOptions ignore_opts = config_opts;
540 ignore_opts.ignore_unknown_options = true;
541
542 std::string options_file_name;
543
544 // Test where the db directory exists but is empty
545 ASSERT_OK(options.env->CreateDir(dbname_));
546 ASSERT_NOK(
547 GetLatestOptionsFileName(dbname_, options.env, &options_file_name));
548 ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
549
550 // Write an options file for a previous major release with an unknown DB
551 // Option
552 WriteOptionsFile(options.env, dbname_, "OPTIONS-0001", ROCKSDB_MAJOR - 1,
553 ROCKSDB_MINOR, "unknown_db_opt=true", "");
554 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
555 ASSERT_NOK(s);
556 ASSERT_TRUE(s.IsInvalidArgument());
557 // Even though ignore_unknown_options=true, we still return an error...
558 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
559 ASSERT_NOK(s);
560 ASSERT_TRUE(s.IsInvalidArgument());
561 // Write an options file for a previous minor release with an unknown CF
562 // Option
563 WriteOptionsFile(options.env, dbname_, "OPTIONS-0002", ROCKSDB_MAJOR,
564 ROCKSDB_MINOR - 1, "", "unknown_cf_opt=true");
565 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
566 ASSERT_NOK(s);
567 ASSERT_TRUE(s.IsInvalidArgument());
568 // Even though ignore_unknown_options=true, we still return an error...
569 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
570 ASSERT_NOK(s);
571 ASSERT_TRUE(s.IsInvalidArgument());
572 // Write an options file for a previous minor release with an unknown BBT
573 // Option
574 WriteOptionsFile(options.env, dbname_, "OPTIONS-0003", ROCKSDB_MAJOR,
575 ROCKSDB_MINOR - 1, "", "", "unknown_bbt_opt=true");
576 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
577 ASSERT_NOK(s);
578 ASSERT_TRUE(s.IsInvalidArgument());
579 // Even though ignore_unknown_options=true, we still return an error...
580 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
581 ASSERT_NOK(s);
582 ASSERT_TRUE(s.IsInvalidArgument());
583
584 // Write an options file for the current release with an unknown DB Option
585 WriteOptionsFile(options.env, dbname_, "OPTIONS-0004", ROCKSDB_MAJOR,
586 ROCKSDB_MINOR, "unknown_db_opt=true", "");
587 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
588 ASSERT_NOK(s);
589 ASSERT_TRUE(s.IsInvalidArgument());
590 // Even though ignore_unknown_options=true, we still return an error...
591 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
592 ASSERT_NOK(s);
593 ASSERT_TRUE(s.IsInvalidArgument());
594
595 // Write an options file for the current release with an unknown CF Option
596 WriteOptionsFile(options.env, dbname_, "OPTIONS-0005", ROCKSDB_MAJOR,
597 ROCKSDB_MINOR, "", "unknown_cf_opt=true");
598 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
599 ASSERT_NOK(s);
600 ASSERT_TRUE(s.IsInvalidArgument());
601 // Even though ignore_unknown_options=true, we still return an error...
602 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
603 ASSERT_NOK(s);
604 ASSERT_TRUE(s.IsInvalidArgument());
605
606 // Write an options file for the current release with an invalid DB Option
607 WriteOptionsFile(options.env, dbname_, "OPTIONS-0006", ROCKSDB_MAJOR,
608 ROCKSDB_MINOR, "create_if_missing=hello", "");
609 s = LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs);
610 ASSERT_NOK(s);
611 ASSERT_TRUE(s.IsInvalidArgument());
612 // Even though ignore_unknown_options=true, we still return an error...
613 s = LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs);
614 ASSERT_NOK(s);
615 ASSERT_TRUE(s.IsInvalidArgument());
616
617 // Write an options file for the next release with an invalid DB Option
618 WriteOptionsFile(options.env, dbname_, "OPTIONS-0007", ROCKSDB_MAJOR,
619 ROCKSDB_MINOR + 1, "create_if_missing=hello", "");
620 ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
621 ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
622
623 // Write an options file for the next release with an unknown DB Option
624 WriteOptionsFile(options.env, dbname_, "OPTIONS-0008", ROCKSDB_MAJOR,
625 ROCKSDB_MINOR + 1, "unknown_db_opt=true", "");
626 ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
627 // Ignore the errors for future releases when ignore_unknown_options=true
628 ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
629
630 // Write an options file for the next major release with an unknown CF Option
631 WriteOptionsFile(options.env, dbname_, "OPTIONS-0009", ROCKSDB_MAJOR + 1,
632 ROCKSDB_MINOR, "", "unknown_cf_opt=true");
633 ASSERT_NOK(LoadLatestOptions(config_opts, dbname_, &db_opts, &cf_descs));
634 // Ignore the errors for future releases when ignore_unknown_options=true
635 ASSERT_OK(LoadLatestOptions(ignore_opts, dbname_, &db_opts, &cf_descs));
636 }
637 } // namespace ROCKSDB_NAMESPACE
638
main(int argc,char ** argv)639 int main(int argc, char** argv) {
640 ::testing::InitGoogleTest(&argc, argv);
641 #ifdef GFLAGS
642 ParseCommandLineFlags(&argc, &argv, true);
643 #endif // GFLAGS
644 return RUN_ALL_TESTS();
645 }
646
647 #else
648 #include <cstdio>
649
main(int,char **)650 int main(int /*argc*/, char** /*argv*/) {
651 printf("Skipped in RocksDBLite as utilities are not supported.\n");
652 return 0;
653 }
654 #endif // !ROCKSDB_LITE
655