]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/options/options_test.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / rocksdb / options / options_test.cc
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under the BSD-style license found in the
3 // LICENSE file in the root directory of this source tree. An additional grant
4 // of patent rights can be found in the PATENTS file in the same 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 #ifndef __STDC_FORMAT_MACROS
11 #define __STDC_FORMAT_MACROS
12 #endif
13
14 #include <cctype>
15 #include <cstring>
16 #include <unordered_map>
17 #include <inttypes.h>
18
19 #include "options/options_helper.h"
20 #include "options/options_parser.h"
21 #include "options/options_sanity_check.h"
22 #include "rocksdb/cache.h"
23 #include "rocksdb/convenience.h"
24 #include "rocksdb/memtablerep.h"
25 #include "rocksdb/utilities/leveldb_options.h"
26 #include "util/random.h"
27 #include "util/stderr_logger.h"
28 #include "util/string_util.h"
29 #include "util/testharness.h"
30 #include "util/testutil.h"
31
32 #ifndef GFLAGS
33 bool FLAGS_enable_print = false;
34 #else
35 #include <gflags/gflags.h>
36 using GFLAGS::ParseCommandLineFlags;
37 DEFINE_bool(enable_print, false, "Print options generated to console.");
38 #endif // GFLAGS
39
40 namespace rocksdb {
41
42 class OptionsTest : public testing::Test {};
43
44 #ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
45 TEST_F(OptionsTest, GetOptionsFromMapTest) {
46 std::unordered_map<std::string, std::string> cf_options_map = {
47 {"write_buffer_size", "1"},
48 {"max_write_buffer_number", "2"},
49 {"min_write_buffer_number_to_merge", "3"},
50 {"max_write_buffer_number_to_maintain", "99"},
51 {"compression", "kSnappyCompression"},
52 {"compression_per_level",
53 "kNoCompression:"
54 "kSnappyCompression:"
55 "kZlibCompression:"
56 "kBZip2Compression:"
57 "kLZ4Compression:"
58 "kLZ4HCCompression:"
59 "kXpressCompression:"
60 "kZSTD:"
61 "kZSTDNotFinalCompression"},
62 {"bottommost_compression", "kLZ4Compression"},
63 {"compression_opts", "4:5:6:7"},
64 {"num_levels", "8"},
65 {"level0_file_num_compaction_trigger", "8"},
66 {"level0_slowdown_writes_trigger", "9"},
67 {"level0_stop_writes_trigger", "10"},
68 {"target_file_size_base", "12"},
69 {"target_file_size_multiplier", "13"},
70 {"max_bytes_for_level_base", "14"},
71 {"level_compaction_dynamic_level_bytes", "true"},
72 {"max_bytes_for_level_multiplier", "15.0"},
73 {"max_bytes_for_level_multiplier_additional", "16:17:18"},
74 {"max_compaction_bytes", "21"},
75 {"soft_rate_limit", "1.1"},
76 {"hard_rate_limit", "2.1"},
77 {"hard_pending_compaction_bytes_limit", "211"},
78 {"arena_block_size", "22"},
79 {"disable_auto_compactions", "true"},
80 {"compaction_style", "kCompactionStyleLevel"},
81 {"compaction_pri", "kOldestSmallestSeqFirst"},
82 {"verify_checksums_in_compaction", "false"},
83 {"compaction_options_fifo", "23"},
84 {"max_sequential_skip_in_iterations", "24"},
85 {"inplace_update_support", "true"},
86 {"report_bg_io_stats", "true"},
87 {"compaction_measure_io_stats", "false"},
88 {"inplace_update_num_locks", "25"},
89 {"memtable_prefix_bloom_size_ratio", "0.26"},
90 {"memtable_huge_page_size", "28"},
91 {"bloom_locality", "29"},
92 {"max_successive_merges", "30"},
93 {"min_partial_merge_operands", "31"},
94 {"prefix_extractor", "fixed:31"},
95 {"optimize_filters_for_hits", "true"},
96 };
97
98 std::unordered_map<std::string, std::string> db_options_map = {
99 {"create_if_missing", "false"},
100 {"create_missing_column_families", "true"},
101 {"error_if_exists", "false"},
102 {"paranoid_checks", "true"},
103 {"max_open_files", "32"},
104 {"max_total_wal_size", "33"},
105 {"use_fsync", "true"},
106 {"db_log_dir", "/db_log_dir"},
107 {"wal_dir", "/wal_dir"},
108 {"delete_obsolete_files_period_micros", "34"},
109 {"max_background_compactions", "35"},
110 {"max_background_flushes", "36"},
111 {"max_log_file_size", "37"},
112 {"log_file_time_to_roll", "38"},
113 {"keep_log_file_num", "39"},
114 {"recycle_log_file_num", "5"},
115 {"max_manifest_file_size", "40"},
116 {"table_cache_numshardbits", "41"},
117 {"WAL_ttl_seconds", "43"},
118 {"WAL_size_limit_MB", "44"},
119 {"manifest_preallocation_size", "45"},
120 {"allow_mmap_reads", "true"},
121 {"allow_mmap_writes", "false"},
122 {"use_direct_reads", "false"},
123 {"use_direct_io_for_flush_and_compaction", "false"},
124 {"is_fd_close_on_exec", "true"},
125 {"skip_log_error_on_recovery", "false"},
126 {"stats_dump_period_sec", "46"},
127 {"advise_random_on_open", "true"},
128 {"use_adaptive_mutex", "false"},
129 {"new_table_reader_for_compaction_inputs", "true"},
130 {"compaction_readahead_size", "100"},
131 {"random_access_max_buffer_size", "3145728"},
132 {"writable_file_max_buffer_size", "314159"},
133 {"bytes_per_sync", "47"},
134 {"wal_bytes_per_sync", "48"},
135 };
136
137 ColumnFamilyOptions base_cf_opt;
138 ColumnFamilyOptions new_cf_opt;
139 ASSERT_OK(GetColumnFamilyOptionsFromMap(
140 base_cf_opt, cf_options_map, &new_cf_opt));
141 ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
142 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
143 ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
144 ASSERT_EQ(new_cf_opt.max_write_buffer_number_to_maintain, 99);
145 ASSERT_EQ(new_cf_opt.compression, kSnappyCompression);
146 ASSERT_EQ(new_cf_opt.compression_per_level.size(), 9U);
147 ASSERT_EQ(new_cf_opt.compression_per_level[0], kNoCompression);
148 ASSERT_EQ(new_cf_opt.compression_per_level[1], kSnappyCompression);
149 ASSERT_EQ(new_cf_opt.compression_per_level[2], kZlibCompression);
150 ASSERT_EQ(new_cf_opt.compression_per_level[3], kBZip2Compression);
151 ASSERT_EQ(new_cf_opt.compression_per_level[4], kLZ4Compression);
152 ASSERT_EQ(new_cf_opt.compression_per_level[5], kLZ4HCCompression);
153 ASSERT_EQ(new_cf_opt.compression_per_level[6], kXpressCompression);
154 ASSERT_EQ(new_cf_opt.compression_per_level[7], kZSTD);
155 ASSERT_EQ(new_cf_opt.compression_per_level[8], kZSTDNotFinalCompression);
156 ASSERT_EQ(new_cf_opt.compression_opts.window_bits, 4);
157 ASSERT_EQ(new_cf_opt.compression_opts.level, 5);
158 ASSERT_EQ(new_cf_opt.compression_opts.strategy, 6);
159 ASSERT_EQ(new_cf_opt.compression_opts.max_dict_bytes, 7);
160 ASSERT_EQ(new_cf_opt.bottommost_compression, kLZ4Compression);
161 ASSERT_EQ(new_cf_opt.num_levels, 8);
162 ASSERT_EQ(new_cf_opt.level0_file_num_compaction_trigger, 8);
163 ASSERT_EQ(new_cf_opt.level0_slowdown_writes_trigger, 9);
164 ASSERT_EQ(new_cf_opt.level0_stop_writes_trigger, 10);
165 ASSERT_EQ(new_cf_opt.target_file_size_base, static_cast<uint64_t>(12));
166 ASSERT_EQ(new_cf_opt.target_file_size_multiplier, 13);
167 ASSERT_EQ(new_cf_opt.max_bytes_for_level_base, 14U);
168 ASSERT_EQ(new_cf_opt.level_compaction_dynamic_level_bytes, true);
169 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier, 15.0);
170 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional.size(), 3U);
171 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[0], 16);
172 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[1], 17);
173 ASSERT_EQ(new_cf_opt.max_bytes_for_level_multiplier_additional[2], 18);
174 ASSERT_EQ(new_cf_opt.max_compaction_bytes, 21);
175 ASSERT_EQ(new_cf_opt.hard_pending_compaction_bytes_limit, 211);
176 ASSERT_EQ(new_cf_opt.arena_block_size, 22U);
177 ASSERT_EQ(new_cf_opt.disable_auto_compactions, true);
178 ASSERT_EQ(new_cf_opt.compaction_style, kCompactionStyleLevel);
179 ASSERT_EQ(new_cf_opt.compaction_pri, kOldestSmallestSeqFirst);
180 ASSERT_EQ(new_cf_opt.compaction_options_fifo.max_table_files_size,
181 static_cast<uint64_t>(23));
182 ASSERT_EQ(new_cf_opt.max_sequential_skip_in_iterations,
183 static_cast<uint64_t>(24));
184 ASSERT_EQ(new_cf_opt.inplace_update_support, true);
185 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 25U);
186 ASSERT_EQ(new_cf_opt.memtable_prefix_bloom_size_ratio, 0.26);
187 ASSERT_EQ(new_cf_opt.memtable_huge_page_size, 28U);
188 ASSERT_EQ(new_cf_opt.bloom_locality, 29U);
189 ASSERT_EQ(new_cf_opt.max_successive_merges, 30U);
190 ASSERT_TRUE(new_cf_opt.prefix_extractor != nullptr);
191 ASSERT_EQ(new_cf_opt.optimize_filters_for_hits, true);
192 ASSERT_EQ(std::string(new_cf_opt.prefix_extractor->Name()),
193 "rocksdb.FixedPrefix.31");
194
195 cf_options_map["write_buffer_size"] = "hello";
196 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
197 base_cf_opt, cf_options_map, &new_cf_opt));
198 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
199
200 cf_options_map["write_buffer_size"] = "1";
201 ASSERT_OK(GetColumnFamilyOptionsFromMap(
202 base_cf_opt, cf_options_map, &new_cf_opt));
203 cf_options_map["unknown_option"] = "1";
204
205 ASSERT_NOK(GetColumnFamilyOptionsFromMap(
206 base_cf_opt, cf_options_map, &new_cf_opt));
207 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
208
209 DBOptions base_db_opt;
210 DBOptions new_db_opt;
211 ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
212 ASSERT_EQ(new_db_opt.create_if_missing, false);
213 ASSERT_EQ(new_db_opt.create_missing_column_families, true);
214 ASSERT_EQ(new_db_opt.error_if_exists, false);
215 ASSERT_EQ(new_db_opt.paranoid_checks, true);
216 ASSERT_EQ(new_db_opt.max_open_files, 32);
217 ASSERT_EQ(new_db_opt.max_total_wal_size, static_cast<uint64_t>(33));
218 ASSERT_EQ(new_db_opt.use_fsync, true);
219 ASSERT_EQ(new_db_opt.db_log_dir, "/db_log_dir");
220 ASSERT_EQ(new_db_opt.wal_dir, "/wal_dir");
221 ASSERT_EQ(new_db_opt.delete_obsolete_files_period_micros,
222 static_cast<uint64_t>(34));
223 ASSERT_EQ(new_db_opt.max_background_compactions, 35);
224 ASSERT_EQ(new_db_opt.max_background_flushes, 36);
225 ASSERT_EQ(new_db_opt.max_log_file_size, 37U);
226 ASSERT_EQ(new_db_opt.log_file_time_to_roll, 38U);
227 ASSERT_EQ(new_db_opt.keep_log_file_num, 39U);
228 ASSERT_EQ(new_db_opt.recycle_log_file_num, 5U);
229 ASSERT_EQ(new_db_opt.max_manifest_file_size, static_cast<uint64_t>(40));
230 ASSERT_EQ(new_db_opt.table_cache_numshardbits, 41);
231 ASSERT_EQ(new_db_opt.WAL_ttl_seconds, static_cast<uint64_t>(43));
232 ASSERT_EQ(new_db_opt.WAL_size_limit_MB, static_cast<uint64_t>(44));
233 ASSERT_EQ(new_db_opt.manifest_preallocation_size, 45U);
234 ASSERT_EQ(new_db_opt.allow_mmap_reads, true);
235 ASSERT_EQ(new_db_opt.allow_mmap_writes, false);
236 ASSERT_EQ(new_db_opt.use_direct_reads, false);
237 ASSERT_EQ(new_db_opt.use_direct_io_for_flush_and_compaction, false);
238 ASSERT_EQ(new_db_opt.is_fd_close_on_exec, true);
239 ASSERT_EQ(new_db_opt.skip_log_error_on_recovery, false);
240 ASSERT_EQ(new_db_opt.stats_dump_period_sec, 46U);
241 ASSERT_EQ(new_db_opt.advise_random_on_open, true);
242 ASSERT_EQ(new_db_opt.use_adaptive_mutex, false);
243 ASSERT_EQ(new_db_opt.new_table_reader_for_compaction_inputs, true);
244 ASSERT_EQ(new_db_opt.compaction_readahead_size, 100);
245 ASSERT_EQ(new_db_opt.random_access_max_buffer_size, 3145728);
246 ASSERT_EQ(new_db_opt.writable_file_max_buffer_size, 314159);
247 ASSERT_EQ(new_db_opt.bytes_per_sync, static_cast<uint64_t>(47));
248 ASSERT_EQ(new_db_opt.wal_bytes_per_sync, static_cast<uint64_t>(48));
249 }
250 #endif // !ROCKSDB_LITE
251
252 #ifndef ROCKSDB_LITE // GetColumnFamilyOptionsFromString is not supported in
253 // ROCKSDB_LITE
254 TEST_F(OptionsTest, GetColumnFamilyOptionsFromStringTest) {
255 ColumnFamilyOptions base_cf_opt;
256 ColumnFamilyOptions new_cf_opt;
257 base_cf_opt.table_factory.reset();
258 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt, "", &new_cf_opt));
259 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
260 "write_buffer_size=5", &new_cf_opt));
261 ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
262 ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
263 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
264 "write_buffer_size=6;", &new_cf_opt));
265 ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
266 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
267 " write_buffer_size = 7 ", &new_cf_opt));
268 ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
269 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
270 " write_buffer_size = 8 ; ", &new_cf_opt));
271 ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
272 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
273 "write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
274 ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
275 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
276 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
277 "write_buffer_size=11; max_write_buffer_number = 12 ;",
278 &new_cf_opt));
279 ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
280 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
281 // Wrong name "max_write_buffer_number_"
282 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
283 "write_buffer_size=13;max_write_buffer_number_=14;",
284 &new_cf_opt));
285 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
286
287 // Wrong key/value pair
288 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
289 "write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
290 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
291
292 // Error Paring value
293 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
294 "write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
295 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
296
297 // Missing option name
298 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
299 "write_buffer_size=13; =100;", &new_cf_opt));
300 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
301
302 const int64_t kilo = 1024UL;
303 const int64_t mega = 1024 * kilo;
304 const int64_t giga = 1024 * mega;
305 const int64_t tera = 1024 * giga;
306
307 // Units (k)
308 ASSERT_OK(GetColumnFamilyOptionsFromString(
309 base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
310 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
311 // Units (m)
312 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
313 "max_write_buffer_number=16m;inplace_update_num_locks=17M",
314 &new_cf_opt));
315 ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
316 ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17 * mega);
317 // Units (g)
318 ASSERT_OK(GetColumnFamilyOptionsFromString(
319 base_cf_opt,
320 "write_buffer_size=18g;prefix_extractor=capped:8;"
321 "arena_block_size=19G",
322 &new_cf_opt));
323
324 ASSERT_EQ(new_cf_opt.write_buffer_size, 18 * giga);
325 ASSERT_EQ(new_cf_opt.arena_block_size, 19 * giga);
326 ASSERT_TRUE(new_cf_opt.prefix_extractor.get() != nullptr);
327 std::string prefix_name(new_cf_opt.prefix_extractor->Name());
328 ASSERT_EQ(prefix_name, "rocksdb.CappedPrefix.8");
329
330 // Units (t)
331 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
332 "write_buffer_size=20t;arena_block_size=21T", &new_cf_opt));
333 ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
334 ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
335
336 // Nested block based table options
337 // Emtpy
338 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
339 "write_buffer_size=10;max_write_buffer_number=16;"
340 "block_based_table_factory={};arena_block_size=1024",
341 &new_cf_opt));
342 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
343 // Non-empty
344 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
345 "write_buffer_size=10;max_write_buffer_number=16;"
346 "block_based_table_factory={block_cache=1M;block_size=4;};"
347 "arena_block_size=1024",
348 &new_cf_opt));
349 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
350 // Last one
351 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
352 "write_buffer_size=10;max_write_buffer_number=16;"
353 "block_based_table_factory={block_cache=1M;block_size=4;}",
354 &new_cf_opt));
355 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
356 // Mismatch curly braces
357 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
358 "write_buffer_size=10;max_write_buffer_number=16;"
359 "block_based_table_factory={{{block_size=4;};"
360 "arena_block_size=1024",
361 &new_cf_opt));
362 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
363
364 // Unexpected chars after closing curly brace
365 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
366 "write_buffer_size=10;max_write_buffer_number=16;"
367 "block_based_table_factory={block_size=4;}};"
368 "arena_block_size=1024",
369 &new_cf_opt));
370 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
371
372 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
373 "write_buffer_size=10;max_write_buffer_number=16;"
374 "block_based_table_factory={block_size=4;}xdfa;"
375 "arena_block_size=1024",
376 &new_cf_opt));
377 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
378
379 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
380 "write_buffer_size=10;max_write_buffer_number=16;"
381 "block_based_table_factory={block_size=4;}xdfa",
382 &new_cf_opt));
383 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
384
385 // Invalid block based table option
386 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
387 "write_buffer_size=10;max_write_buffer_number=16;"
388 "block_based_table_factory={xx_block_size=4;}",
389 &new_cf_opt));
390 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
391
392 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
393 "optimize_filters_for_hits=true",
394 &new_cf_opt));
395 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
396 "optimize_filters_for_hits=false",
397 &new_cf_opt));
398
399 ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
400 "optimize_filters_for_hits=junk",
401 &new_cf_opt));
402 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_cf_opt, new_cf_opt));
403
404 // Nested plain table options
405 // Emtpy
406 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
407 "write_buffer_size=10;max_write_buffer_number=16;"
408 "plain_table_factory={};arena_block_size=1024",
409 &new_cf_opt));
410 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
411 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
412 // Non-empty
413 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
414 "write_buffer_size=10;max_write_buffer_number=16;"
415 "plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
416 "arena_block_size=1024",
417 &new_cf_opt));
418 ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
419 ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
420
421 // memtable factory
422 ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
423 "write_buffer_size=10;max_write_buffer_number=16;"
424 "memtable=skip_list:10;arena_block_size=1024",
425 &new_cf_opt));
426 ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
427 ASSERT_EQ(std::string(new_cf_opt.memtable_factory->Name()), "SkipListFactory");
428 }
429 #endif // !ROCKSDB_LITE
430
431 #ifndef ROCKSDB_LITE // GetBlockBasedTableOptionsFromString is not supported
432 TEST_F(OptionsTest, GetBlockBasedTableOptionsFromString) {
433 BlockBasedTableOptions table_opt;
434 BlockBasedTableOptions new_opt;
435 // make sure default values are overwritten by something else
436 ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
437 "cache_index_and_filter_blocks=1;index_type=kHashSearch;"
438 "checksum=kxxHash;hash_index_allow_collision=1;no_block_cache=1;"
439 "block_cache=1M;block_cache_compressed=1k;block_size=1024;"
440 "block_size_deviation=8;block_restart_interval=4;"
441 "filter_policy=bloomfilter:4:true;whole_key_filtering=1;",
442 &new_opt));
443 ASSERT_TRUE(new_opt.cache_index_and_filter_blocks);
444 ASSERT_EQ(new_opt.index_type, BlockBasedTableOptions::kHashSearch);
445 ASSERT_EQ(new_opt.checksum, ChecksumType::kxxHash);
446 ASSERT_TRUE(new_opt.hash_index_allow_collision);
447 ASSERT_TRUE(new_opt.no_block_cache);
448 ASSERT_TRUE(new_opt.block_cache != nullptr);
449 ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
450 ASSERT_TRUE(new_opt.block_cache_compressed != nullptr);
451 ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL);
452 ASSERT_EQ(new_opt.block_size, 1024UL);
453 ASSERT_EQ(new_opt.block_size_deviation, 8);
454 ASSERT_EQ(new_opt.block_restart_interval, 4);
455 ASSERT_TRUE(new_opt.filter_policy != nullptr);
456
457 // unknown option
458 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
459 "cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
460 "bad_option=1",
461 &new_opt));
462 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
463 new_opt.cache_index_and_filter_blocks);
464 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
465
466 // unrecognized index type
467 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
468 "cache_index_and_filter_blocks=1;index_type=kBinarySearchXX",
469 &new_opt));
470 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
471 new_opt.cache_index_and_filter_blocks);
472 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
473
474 // unrecognized checksum type
475 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
476 "cache_index_and_filter_blocks=1;checksum=kxxHashXX",
477 &new_opt));
478 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
479 new_opt.cache_index_and_filter_blocks);
480 ASSERT_EQ(table_opt.index_type, new_opt.index_type);
481
482 // unrecognized filter policy name
483 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
484 "cache_index_and_filter_blocks=1;"
485 "filter_policy=bloomfilterxx:4:true",
486 &new_opt));
487 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
488 new_opt.cache_index_and_filter_blocks);
489 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
490
491 // unrecognized filter policy config
492 ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
493 "cache_index_and_filter_blocks=1;"
494 "filter_policy=bloomfilter:4",
495 &new_opt));
496 ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
497 new_opt.cache_index_and_filter_blocks);
498 ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
499 }
500 #endif // !ROCKSDB_LITE
501
502
503 #ifndef ROCKSDB_LITE // GetPlainTableOptionsFromString is not supported
504 TEST_F(OptionsTest, GetPlainTableOptionsFromString) {
505 PlainTableOptions table_opt;
506 PlainTableOptions new_opt;
507 // make sure default values are overwritten by something else
508 ASSERT_OK(GetPlainTableOptionsFromString(table_opt,
509 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
510 "index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
511 "full_scan_mode=true;store_index_in_file=true",
512 &new_opt));
513 ASSERT_EQ(new_opt.user_key_len, 66);
514 ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
515 ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
516 ASSERT_EQ(new_opt.index_sparseness, 8);
517 ASSERT_EQ(new_opt.huge_page_tlb_size, 4);
518 ASSERT_EQ(new_opt.encoding_type, EncodingType::kPrefix);
519 ASSERT_TRUE(new_opt.full_scan_mode);
520 ASSERT_TRUE(new_opt.store_index_in_file);
521
522 // unknown option
523 ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
524 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
525 "bad_option=1",
526 &new_opt));
527
528 // unrecognized EncodingType
529 ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
530 "user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
531 "encoding_type=kPrefixXX",
532 &new_opt));
533 }
534 #endif // !ROCKSDB_LITE
535
536 #ifndef ROCKSDB_LITE // GetMemTableRepFactoryFromString is not supported
537 TEST_F(OptionsTest, GetMemTableRepFactoryFromString) {
538 std::unique_ptr<MemTableRepFactory> new_mem_factory = nullptr;
539
540 ASSERT_OK(GetMemTableRepFactoryFromString("skip_list", &new_mem_factory));
541 ASSERT_OK(GetMemTableRepFactoryFromString("skip_list:16", &new_mem_factory));
542 ASSERT_EQ(std::string(new_mem_factory->Name()), "SkipListFactory");
543 ASSERT_NOK(GetMemTableRepFactoryFromString("skip_list:16:invalid_opt",
544 &new_mem_factory));
545
546 ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash", &new_mem_factory));
547 ASSERT_OK(GetMemTableRepFactoryFromString("prefix_hash:1000",
548 &new_mem_factory));
549 ASSERT_EQ(std::string(new_mem_factory->Name()), "HashSkipListRepFactory");
550 ASSERT_NOK(GetMemTableRepFactoryFromString("prefix_hash:1000:invalid_opt",
551 &new_mem_factory));
552
553 ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist",
554 &new_mem_factory));
555 ASSERT_OK(GetMemTableRepFactoryFromString("hash_linkedlist:1000",
556 &new_mem_factory));
557 ASSERT_EQ(std::string(new_mem_factory->Name()), "HashLinkListRepFactory");
558 ASSERT_NOK(GetMemTableRepFactoryFromString("hash_linkedlist:1000:invalid_opt",
559 &new_mem_factory));
560
561 ASSERT_OK(GetMemTableRepFactoryFromString("vector", &new_mem_factory));
562 ASSERT_OK(GetMemTableRepFactoryFromString("vector:1024", &new_mem_factory));
563 ASSERT_EQ(std::string(new_mem_factory->Name()), "VectorRepFactory");
564 ASSERT_NOK(GetMemTableRepFactoryFromString("vector:1024:invalid_opt",
565 &new_mem_factory));
566
567 ASSERT_NOK(GetMemTableRepFactoryFromString("cuckoo", &new_mem_factory));
568 ASSERT_OK(GetMemTableRepFactoryFromString("cuckoo:1024", &new_mem_factory));
569 ASSERT_EQ(std::string(new_mem_factory->Name()), "HashCuckooRepFactory");
570
571 ASSERT_NOK(GetMemTableRepFactoryFromString("bad_factory", &new_mem_factory));
572 }
573 #endif // !ROCKSDB_LITE
574
575 #ifndef ROCKSDB_LITE // GetOptionsFromString is not supported in RocksDB Lite
576 TEST_F(OptionsTest, GetOptionsFromStringTest) {
577 Options base_options, new_options;
578 base_options.write_buffer_size = 20;
579 base_options.min_write_buffer_number_to_merge = 15;
580 BlockBasedTableOptions block_based_table_options;
581 block_based_table_options.cache_index_and_filter_blocks = true;
582 base_options.table_factory.reset(
583 NewBlockBasedTableFactory(block_based_table_options));
584 ASSERT_OK(GetOptionsFromString(
585 base_options,
586 "write_buffer_size=10;max_write_buffer_number=16;"
587 "block_based_table_factory={block_cache=1M;block_size=4;};"
588 "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
589 "rate_limiter_bytes_per_sec=1024",
590 &new_options));
591
592 ASSERT_EQ(new_options.compression_opts.window_bits, 4);
593 ASSERT_EQ(new_options.compression_opts.level, 5);
594 ASSERT_EQ(new_options.compression_opts.strategy, 6);
595 ASSERT_EQ(new_options.compression_opts.max_dict_bytes, 0);
596 ASSERT_EQ(new_options.bottommost_compression, kDisableCompressionOption);
597 ASSERT_EQ(new_options.write_buffer_size, 10U);
598 ASSERT_EQ(new_options.max_write_buffer_number, 16);
599 BlockBasedTableOptions new_block_based_table_options =
600 dynamic_cast<BlockBasedTableFactory*>(new_options.table_factory.get())
601 ->table_options();
602 ASSERT_EQ(new_block_based_table_options.block_cache->GetCapacity(), 1U << 20);
603 ASSERT_EQ(new_block_based_table_options.block_size, 4U);
604 // don't overwrite block based table options
605 ASSERT_TRUE(new_block_based_table_options.cache_index_and_filter_blocks);
606
607 ASSERT_EQ(new_options.create_if_missing, true);
608 ASSERT_EQ(new_options.max_open_files, 1);
609 ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
610 }
611
612 TEST_F(OptionsTest, DBOptionsSerialization) {
613 Options base_options, new_options;
614 Random rnd(301);
615
616 // Phase 1: Make big change in base_options
617 test::RandomInitDBOptions(&base_options, &rnd);
618
619 // Phase 2: obtain a string from base_option
620 std::string base_options_file_content;
621 ASSERT_OK(GetStringFromDBOptions(&base_options_file_content, base_options));
622
623 // Phase 3: Set new_options from the derived string and expect
624 // new_options == base_options
625 ASSERT_OK(GetDBOptionsFromString(DBOptions(), base_options_file_content,
626 &new_options));
627 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(base_options, new_options));
628 }
629
630 TEST_F(OptionsTest, ColumnFamilyOptionsSerialization) {
631 ColumnFamilyOptions base_opt, new_opt;
632 Random rnd(302);
633 // Phase 1: randomly assign base_opt
634 // custom type options
635 test::RandomInitCFOptions(&base_opt, &rnd);
636
637 // Phase 2: obtain a string from base_opt
638 std::string base_options_file_content;
639 ASSERT_OK(
640 GetStringFromColumnFamilyOptions(&base_options_file_content, base_opt));
641
642 // Phase 3: Set new_opt from the derived string and expect
643 // new_opt == base_opt
644 ASSERT_OK(GetColumnFamilyOptionsFromString(
645 ColumnFamilyOptions(), base_options_file_content, &new_opt));
646 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(base_opt, new_opt));
647 if (base_opt.compaction_filter) {
648 delete base_opt.compaction_filter;
649 }
650 }
651
652 #endif // !ROCKSDB_LITE
653
654 Status StringToMap(
655 const std::string& opts_str,
656 std::unordered_map<std::string, std::string>* opts_map);
657
658 #ifndef ROCKSDB_LITE // StringToMap is not supported in ROCKSDB_LITE
659 TEST_F(OptionsTest, StringToMapTest) {
660 std::unordered_map<std::string, std::string> opts_map;
661 // Regular options
662 ASSERT_OK(StringToMap("k1=v1;k2=v2;k3=v3", &opts_map));
663 ASSERT_EQ(opts_map["k1"], "v1");
664 ASSERT_EQ(opts_map["k2"], "v2");
665 ASSERT_EQ(opts_map["k3"], "v3");
666 // Value with '='
667 opts_map.clear();
668 ASSERT_OK(StringToMap("k1==v1;k2=v2=;", &opts_map));
669 ASSERT_EQ(opts_map["k1"], "=v1");
670 ASSERT_EQ(opts_map["k2"], "v2=");
671 // Overwrriten option
672 opts_map.clear();
673 ASSERT_OK(StringToMap("k1=v1;k1=v2;k3=v3", &opts_map));
674 ASSERT_EQ(opts_map["k1"], "v2");
675 ASSERT_EQ(opts_map["k3"], "v3");
676 // Empty value
677 opts_map.clear();
678 ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4=", &opts_map));
679 ASSERT_EQ(opts_map["k1"], "v1");
680 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
681 ASSERT_EQ(opts_map["k2"], "");
682 ASSERT_EQ(opts_map["k3"], "v3");
683 ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
684 ASSERT_EQ(opts_map["k4"], "");
685 opts_map.clear();
686 ASSERT_OK(StringToMap("k1=v1;k2=;k3=v3;k4= ", &opts_map));
687 ASSERT_EQ(opts_map["k1"], "v1");
688 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
689 ASSERT_EQ(opts_map["k2"], "");
690 ASSERT_EQ(opts_map["k3"], "v3");
691 ASSERT_TRUE(opts_map.find("k4") != opts_map.end());
692 ASSERT_EQ(opts_map["k4"], "");
693 opts_map.clear();
694 ASSERT_OK(StringToMap("k1=v1;k2=;k3=", &opts_map));
695 ASSERT_EQ(opts_map["k1"], "v1");
696 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
697 ASSERT_EQ(opts_map["k2"], "");
698 ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
699 ASSERT_EQ(opts_map["k3"], "");
700 opts_map.clear();
701 ASSERT_OK(StringToMap("k1=v1;k2=;k3=;", &opts_map));
702 ASSERT_EQ(opts_map["k1"], "v1");
703 ASSERT_TRUE(opts_map.find("k2") != opts_map.end());
704 ASSERT_EQ(opts_map["k2"], "");
705 ASSERT_TRUE(opts_map.find("k3") != opts_map.end());
706 ASSERT_EQ(opts_map["k3"], "");
707 // Regular nested options
708 opts_map.clear();
709 ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2=nv2};k3=v3", &opts_map));
710 ASSERT_EQ(opts_map["k1"], "v1");
711 ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2=nv2");
712 ASSERT_EQ(opts_map["k3"], "v3");
713 // Multi-level nested options
714 opts_map.clear();
715 ASSERT_OK(StringToMap("k1=v1;k2={nk1=nv1;nk2={nnk1=nnk2}};"
716 "k3={nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}};k4=v4",
717 &opts_map));
718 ASSERT_EQ(opts_map["k1"], "v1");
719 ASSERT_EQ(opts_map["k2"], "nk1=nv1;nk2={nnk1=nnk2}");
720 ASSERT_EQ(opts_map["k3"], "nk1={nnk1={nnnk1=nnnv1;nnnk2;nnnv2}}");
721 ASSERT_EQ(opts_map["k4"], "v4");
722 // Garbage inside curly braces
723 opts_map.clear();
724 ASSERT_OK(StringToMap("k1=v1;k2={dfad=};k3={=};k4=v4",
725 &opts_map));
726 ASSERT_EQ(opts_map["k1"], "v1");
727 ASSERT_EQ(opts_map["k2"], "dfad=");
728 ASSERT_EQ(opts_map["k3"], "=");
729 ASSERT_EQ(opts_map["k4"], "v4");
730 // Empty nested options
731 opts_map.clear();
732 ASSERT_OK(StringToMap("k1=v1;k2={};", &opts_map));
733 ASSERT_EQ(opts_map["k1"], "v1");
734 ASSERT_EQ(opts_map["k2"], "");
735 opts_map.clear();
736 ASSERT_OK(StringToMap("k1=v1;k2={{{{}}}{}{}};", &opts_map));
737 ASSERT_EQ(opts_map["k1"], "v1");
738 ASSERT_EQ(opts_map["k2"], "{{{}}}{}{}");
739 // With random spaces
740 opts_map.clear();
741 ASSERT_OK(StringToMap(" k1 = v1 ; k2= {nk1=nv1; nk2={nnk1=nnk2}} ; "
742 "k3={ { } }; k4= v4 ",
743 &opts_map));
744 ASSERT_EQ(opts_map["k1"], "v1");
745 ASSERT_EQ(opts_map["k2"], "nk1=nv1; nk2={nnk1=nnk2}");
746 ASSERT_EQ(opts_map["k3"], "{ }");
747 ASSERT_EQ(opts_map["k4"], "v4");
748
749 // Empty key
750 ASSERT_NOK(StringToMap("k1=v1;k2=v2;=", &opts_map));
751 ASSERT_NOK(StringToMap("=v1;k2=v2", &opts_map));
752 ASSERT_NOK(StringToMap("k1=v1;k2v2;", &opts_map));
753 ASSERT_NOK(StringToMap("k1=v1;k2=v2;fadfa", &opts_map));
754 ASSERT_NOK(StringToMap("k1=v1;k2=v2;;", &opts_map));
755 // Mismatch curly braces
756 ASSERT_NOK(StringToMap("k1=v1;k2={;k3=v3", &opts_map));
757 ASSERT_NOK(StringToMap("k1=v1;k2={{};k3=v3", &opts_map));
758 ASSERT_NOK(StringToMap("k1=v1;k2={}};k3=v3", &opts_map));
759 ASSERT_NOK(StringToMap("k1=v1;k2={{}{}}};k3=v3", &opts_map));
760 // However this is valid!
761 opts_map.clear();
762 ASSERT_OK(StringToMap("k1=v1;k2=};k3=v3", &opts_map));
763 ASSERT_EQ(opts_map["k1"], "v1");
764 ASSERT_EQ(opts_map["k2"], "}");
765 ASSERT_EQ(opts_map["k3"], "v3");
766
767 // Invalid chars after closing curly brace
768 ASSERT_NOK(StringToMap("k1=v1;k2={{}}{};k3=v3", &opts_map));
769 ASSERT_NOK(StringToMap("k1=v1;k2={{}}cfda;k3=v3", &opts_map));
770 ASSERT_NOK(StringToMap("k1=v1;k2={{}} cfda;k3=v3", &opts_map));
771 ASSERT_NOK(StringToMap("k1=v1;k2={{}} cfda", &opts_map));
772 ASSERT_NOK(StringToMap("k1=v1;k2={{}}{}", &opts_map));
773 ASSERT_NOK(StringToMap("k1=v1;k2={{dfdl}adfa}{}", &opts_map));
774 }
775 #endif // ROCKSDB_LITE
776
777 #ifndef ROCKSDB_LITE // StringToMap is not supported in ROCKSDB_LITE
778 TEST_F(OptionsTest, StringToMapRandomTest) {
779 std::unordered_map<std::string, std::string> opts_map;
780 // Make sure segfault is not hit by semi-random strings
781
782 std::vector<std::string> bases = {
783 "a={aa={};tt={xxx={}}};c=defff",
784 "a={aa={};tt={xxx={}}};c=defff;d={{}yxx{}3{xx}}",
785 "abc={{}{}{}{{{}}}{{}{}{}{}{}{}{}"};
786
787 for (std::string base : bases) {
788 for (int rand_seed = 301; rand_seed < 401; rand_seed++) {
789 Random rnd(rand_seed);
790 for (int attempt = 0; attempt < 10; attempt++) {
791 std::string str = base;
792 // Replace random position to space
793 size_t pos = static_cast<size_t>(
794 rnd.Uniform(static_cast<int>(base.size())));
795 str[pos] = ' ';
796 Status s = StringToMap(str, &opts_map);
797 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
798 opts_map.clear();
799 }
800 }
801 }
802
803 // Random Construct a string
804 std::vector<char> chars = {'{', '}', ' ', '=', ';', 'c'};
805 for (int rand_seed = 301; rand_seed < 1301; rand_seed++) {
806 Random rnd(rand_seed);
807 int len = rnd.Uniform(30);
808 std::string str = "";
809 for (int attempt = 0; attempt < len; attempt++) {
810 // Add a random character
811 size_t pos = static_cast<size_t>(
812 rnd.Uniform(static_cast<int>(chars.size())));
813 str.append(1, chars[pos]);
814 }
815 Status s = StringToMap(str, &opts_map);
816 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
817 s = StringToMap("name=" + str, &opts_map);
818 ASSERT_TRUE(s.ok() || s.IsInvalidArgument());
819 opts_map.clear();
820 }
821 }
822
823 TEST_F(OptionsTest, GetStringFromCompressionType) {
824 std::string res;
825
826 ASSERT_OK(GetStringFromCompressionType(&res, kNoCompression));
827 ASSERT_EQ(res, "kNoCompression");
828
829 ASSERT_OK(GetStringFromCompressionType(&res, kSnappyCompression));
830 ASSERT_EQ(res, "kSnappyCompression");
831
832 ASSERT_OK(GetStringFromCompressionType(&res, kDisableCompressionOption));
833 ASSERT_EQ(res, "kDisableCompressionOption");
834
835 ASSERT_OK(GetStringFromCompressionType(&res, kLZ4Compression));
836 ASSERT_EQ(res, "kLZ4Compression");
837
838 ASSERT_OK(GetStringFromCompressionType(&res, kZlibCompression));
839 ASSERT_EQ(res, "kZlibCompression");
840
841 ASSERT_NOK(
842 GetStringFromCompressionType(&res, static_cast<CompressionType>(-10)));
843 }
844 #endif // !ROCKSDB_LITE
845
846 TEST_F(OptionsTest, ConvertOptionsTest) {
847 LevelDBOptions leveldb_opt;
848 Options converted_opt = ConvertOptions(leveldb_opt);
849
850 ASSERT_EQ(converted_opt.create_if_missing, leveldb_opt.create_if_missing);
851 ASSERT_EQ(converted_opt.error_if_exists, leveldb_opt.error_if_exists);
852 ASSERT_EQ(converted_opt.paranoid_checks, leveldb_opt.paranoid_checks);
853 ASSERT_EQ(converted_opt.env, leveldb_opt.env);
854 ASSERT_EQ(converted_opt.info_log.get(), leveldb_opt.info_log);
855 ASSERT_EQ(converted_opt.write_buffer_size, leveldb_opt.write_buffer_size);
856 ASSERT_EQ(converted_opt.max_open_files, leveldb_opt.max_open_files);
857 ASSERT_EQ(converted_opt.compression, leveldb_opt.compression);
858
859 std::shared_ptr<BlockBasedTableFactory> table_factory =
860 std::dynamic_pointer_cast<BlockBasedTableFactory>(
861 converted_opt.table_factory);
862
863 ASSERT_TRUE(table_factory.get() != nullptr);
864
865 const BlockBasedTableOptions table_opt = table_factory->table_options();
866
867 ASSERT_EQ(table_opt.block_cache->GetCapacity(), 8UL << 20);
868 ASSERT_EQ(table_opt.block_size, leveldb_opt.block_size);
869 ASSERT_EQ(table_opt.block_restart_interval,
870 leveldb_opt.block_restart_interval);
871 ASSERT_EQ(table_opt.filter_policy.get(), leveldb_opt.filter_policy);
872 }
873
874 #ifndef ROCKSDB_LITE
875 class OptionsParserTest : public testing::Test {
876 public:
877 OptionsParserTest() { env_.reset(new test::StringEnv(Env::Default())); }
878
879 protected:
880 std::unique_ptr<test::StringEnv> env_;
881 };
882
883 TEST_F(OptionsParserTest, Comment) {
884 DBOptions db_opt;
885 db_opt.max_open_files = 12345;
886 db_opt.max_background_flushes = 301;
887 db_opt.max_total_wal_size = 1024;
888 ColumnFamilyOptions cf_opt;
889
890 std::string options_file_content =
891 "# This is a testing option string.\n"
892 "# Currently we only support \"#\" styled comment.\n"
893 "\n"
894 "[Version]\n"
895 " rocksdb_version=3.14.0\n"
896 " options_file_version=1\n"
897 "[ DBOptions ]\n"
898 " # note that we don't support space around \"=\"\n"
899 " max_open_files=12345;\n"
900 " max_background_flushes=301 # comment after a statement is fine\n"
901 " # max_background_flushes=1000 # this line would be ignored\n"
902 " # max_background_compactions=2000 # so does this one\n"
903 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
904 "[CFOptions \"default\"] # column family must be specified\n"
905 " # in the correct order\n"
906 " # if a section is blank, we will use the default\n";
907
908 const std::string kTestFileName = "test-rocksdb-options.ini";
909 env_->WriteToNewFile(kTestFileName, options_file_content);
910 RocksDBOptionsParser parser;
911 ASSERT_OK(parser.Parse(kTestFileName, env_.get()));
912
913 ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(*parser.db_opt(), db_opt));
914 ASSERT_EQ(parser.NumColumnFamilies(), 1U);
915 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
916 *parser.GetCFOptions("default"), cf_opt));
917 }
918
919 TEST_F(OptionsParserTest, ExtraSpace) {
920 std::string options_file_content =
921 "# This is a testing option string.\n"
922 "# Currently we only support \"#\" styled comment.\n"
923 "\n"
924 "[ Version ]\n"
925 " rocksdb_version = 3.14.0 \n"
926 " options_file_version=1 # some comment\n"
927 "[DBOptions ] # some comment\n"
928 "max_open_files=12345 \n"
929 " max_background_flushes = 301 \n"
930 " max_total_wal_size = 1024 # keep_log_file_num=1000\n"
931 " [CFOptions \"default\" ]\n"
932 " # if a section is blank, we will use the default\n";
933
934 const std::string kTestFileName = "test-rocksdb-options.ini";
935 env_->WriteToNewFile(kTestFileName, options_file_content);
936 RocksDBOptionsParser parser;
937 ASSERT_OK(parser.Parse(kTestFileName, env_.get()));
938 }
939
940 TEST_F(OptionsParserTest, MissingDBOptions) {
941 std::string options_file_content =
942 "# This is a testing option string.\n"
943 "# Currently we only support \"#\" styled comment.\n"
944 "\n"
945 "[Version]\n"
946 " rocksdb_version=3.14.0\n"
947 " options_file_version=1\n"
948 "[CFOptions \"default\"]\n"
949 " # if a section is blank, we will use the default\n";
950
951 const std::string kTestFileName = "test-rocksdb-options.ini";
952 env_->WriteToNewFile(kTestFileName, options_file_content);
953 RocksDBOptionsParser parser;
954 ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
955 }
956
957 TEST_F(OptionsParserTest, DoubleDBOptions) {
958 DBOptions db_opt;
959 db_opt.max_open_files = 12345;
960 db_opt.max_background_flushes = 301;
961 db_opt.max_total_wal_size = 1024;
962 ColumnFamilyOptions cf_opt;
963
964 std::string options_file_content =
965 "# This is a testing option string.\n"
966 "# Currently we only support \"#\" styled comment.\n"
967 "\n"
968 "[Version]\n"
969 " rocksdb_version=3.14.0\n"
970 " options_file_version=1\n"
971 "[DBOptions]\n"
972 " max_open_files=12345\n"
973 " max_background_flushes=301\n"
974 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
975 "[DBOptions]\n"
976 "[CFOptions \"default\"]\n"
977 " # if a section is blank, we will use the default\n";
978
979 const std::string kTestFileName = "test-rocksdb-options.ini";
980 env_->WriteToNewFile(kTestFileName, options_file_content);
981 RocksDBOptionsParser parser;
982 ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
983 }
984
985 TEST_F(OptionsParserTest, NoDefaultCFOptions) {
986 DBOptions db_opt;
987 db_opt.max_open_files = 12345;
988 db_opt.max_background_flushes = 301;
989 db_opt.max_total_wal_size = 1024;
990 ColumnFamilyOptions cf_opt;
991
992 std::string options_file_content =
993 "# This is a testing option string.\n"
994 "# Currently we only support \"#\" styled comment.\n"
995 "\n"
996 "[Version]\n"
997 " rocksdb_version=3.14.0\n"
998 " options_file_version=1\n"
999 "[DBOptions]\n"
1000 " max_open_files=12345\n"
1001 " max_background_flushes=301\n"
1002 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
1003 "[CFOptions \"something_else\"]\n"
1004 " # if a section is blank, we will use the default\n";
1005
1006 const std::string kTestFileName = "test-rocksdb-options.ini";
1007 env_->WriteToNewFile(kTestFileName, options_file_content);
1008 RocksDBOptionsParser parser;
1009 ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1010 }
1011
1012 TEST_F(OptionsParserTest, DefaultCFOptionsMustBeTheFirst) {
1013 DBOptions db_opt;
1014 db_opt.max_open_files = 12345;
1015 db_opt.max_background_flushes = 301;
1016 db_opt.max_total_wal_size = 1024;
1017 ColumnFamilyOptions cf_opt;
1018
1019 std::string options_file_content =
1020 "# This is a testing option string.\n"
1021 "# Currently we only support \"#\" styled comment.\n"
1022 "\n"
1023 "[Version]\n"
1024 " rocksdb_version=3.14.0\n"
1025 " options_file_version=1\n"
1026 "[DBOptions]\n"
1027 " max_open_files=12345\n"
1028 " max_background_flushes=301\n"
1029 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
1030 "[CFOptions \"something_else\"]\n"
1031 " # if a section is blank, we will use the default\n"
1032 "[CFOptions \"default\"]\n"
1033 " # if a section is blank, we will use the default\n";
1034
1035 const std::string kTestFileName = "test-rocksdb-options.ini";
1036 env_->WriteToNewFile(kTestFileName, options_file_content);
1037 RocksDBOptionsParser parser;
1038 ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1039 }
1040
1041 TEST_F(OptionsParserTest, DuplicateCFOptions) {
1042 DBOptions db_opt;
1043 db_opt.max_open_files = 12345;
1044 db_opt.max_background_flushes = 301;
1045 db_opt.max_total_wal_size = 1024;
1046 ColumnFamilyOptions cf_opt;
1047
1048 std::string options_file_content =
1049 "# This is a testing option string.\n"
1050 "# Currently we only support \"#\" styled comment.\n"
1051 "\n"
1052 "[Version]\n"
1053 " rocksdb_version=3.14.0\n"
1054 " options_file_version=1\n"
1055 "[DBOptions]\n"
1056 " max_open_files=12345\n"
1057 " max_background_flushes=301\n"
1058 " max_total_wal_size=1024 # keep_log_file_num=1000\n"
1059 "[CFOptions \"default\"]\n"
1060 "[CFOptions \"something_else\"]\n"
1061 "[CFOptions \"something_else\"]\n";
1062
1063 const std::string kTestFileName = "test-rocksdb-options.ini";
1064 env_->WriteToNewFile(kTestFileName, options_file_content);
1065 RocksDBOptionsParser parser;
1066 ASSERT_NOK(parser.Parse(kTestFileName, env_.get()));
1067 }
1068
1069 TEST_F(OptionsParserTest, ParseVersion) {
1070 DBOptions db_opt;
1071 db_opt.max_open_files = 12345;
1072 db_opt.max_background_flushes = 301;
1073 db_opt.max_total_wal_size = 1024;
1074 ColumnFamilyOptions cf_opt;
1075
1076 std::string file_template =
1077 "# This is a testing option string.\n"
1078 "# Currently we only support \"#\" styled comment.\n"
1079 "\n"
1080 "[Version]\n"
1081 " rocksdb_version=3.13.1\n"
1082 " options_file_version=%s\n"
1083 "[DBOptions]\n"
1084 "[CFOptions \"default\"]\n";
1085 const int kLength = 1000;
1086 char buffer[kLength];
1087 RocksDBOptionsParser parser;
1088
1089 const std::vector<std::string> invalid_versions = {
1090 "a.b.c", "3.2.2b", "3.-12", "3. 1", // only digits and dots are allowed
1091 "1.2.3.4",
1092 "1.2.3" // can only contains at most one dot.
1093 "0", // options_file_version must be at least one
1094 "3..2",
1095 ".", ".1.2", // must have at least one digit before each dot
1096 "1.2.", "1.", "2.34."}; // must have at least one digit after each dot
1097 for (auto iv : invalid_versions) {
1098 snprintf(buffer, kLength - 1, file_template.c_str(), iv.c_str());
1099
1100 parser.Reset();
1101 env_->WriteToNewFile(iv, buffer);
1102 ASSERT_NOK(parser.Parse(iv, env_.get()));
1103 }
1104
1105 const std::vector<std::string> valid_versions = {
1106 "1.232", "100", "3.12", "1", "12.3 ", " 1.25 "};
1107 for (auto vv : valid_versions) {
1108 snprintf(buffer, kLength - 1, file_template.c_str(), vv.c_str());
1109 parser.Reset();
1110 env_->WriteToNewFile(vv, buffer);
1111 ASSERT_OK(parser.Parse(vv, env_.get()));
1112 }
1113 }
1114
1115 void VerifyCFPointerTypedOptions(
1116 ColumnFamilyOptions* base_cf_opt, const ColumnFamilyOptions* new_cf_opt,
1117 const std::unordered_map<std::string, std::string>* new_cf_opt_map) {
1118 std::string name_buffer;
1119 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1120 new_cf_opt_map));
1121
1122 // change the name of merge operator back-and-forth
1123 {
1124 auto* merge_operator = dynamic_cast<test::ChanglingMergeOperator*>(
1125 base_cf_opt->merge_operator.get());
1126 if (merge_operator != nullptr) {
1127 name_buffer = merge_operator->Name();
1128 // change the name and expect non-ok status
1129 merge_operator->SetName("some-other-name");
1130 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1131 *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1132 // change the name back and expect ok status
1133 merge_operator->SetName(name_buffer);
1134 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1135 new_cf_opt_map));
1136 }
1137 }
1138
1139 // change the name of the compaction filter factory back-and-forth
1140 {
1141 auto* compaction_filter_factory =
1142 dynamic_cast<test::ChanglingCompactionFilterFactory*>(
1143 base_cf_opt->compaction_filter_factory.get());
1144 if (compaction_filter_factory != nullptr) {
1145 name_buffer = compaction_filter_factory->Name();
1146 // change the name and expect non-ok status
1147 compaction_filter_factory->SetName("some-other-name");
1148 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1149 *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1150 // change the name back and expect ok status
1151 compaction_filter_factory->SetName(name_buffer);
1152 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1153 new_cf_opt_map));
1154 }
1155 }
1156
1157 // test by setting compaction_filter to nullptr
1158 {
1159 auto* tmp_compaction_filter = base_cf_opt->compaction_filter;
1160 if (tmp_compaction_filter != nullptr) {
1161 base_cf_opt->compaction_filter = nullptr;
1162 // set compaction_filter to nullptr and expect non-ok status
1163 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1164 *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1165 // set the value back and expect ok status
1166 base_cf_opt->compaction_filter = tmp_compaction_filter;
1167 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1168 new_cf_opt_map));
1169 }
1170 }
1171
1172 // test by setting table_factory to nullptr
1173 {
1174 auto tmp_table_factory = base_cf_opt->table_factory;
1175 if (tmp_table_factory != nullptr) {
1176 base_cf_opt->table_factory.reset();
1177 // set table_factory to nullptr and expect non-ok status
1178 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1179 *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1180 // set the value back and expect ok status
1181 base_cf_opt->table_factory = tmp_table_factory;
1182 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1183 new_cf_opt_map));
1184 }
1185 }
1186
1187 // test by setting memtable_factory to nullptr
1188 {
1189 auto tmp_memtable_factory = base_cf_opt->memtable_factory;
1190 if (tmp_memtable_factory != nullptr) {
1191 base_cf_opt->memtable_factory.reset();
1192 // set memtable_factory to nullptr and expect non-ok status
1193 ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
1194 *base_cf_opt, *new_cf_opt, new_cf_opt_map));
1195 // set the value back and expect ok status
1196 base_cf_opt->memtable_factory = tmp_memtable_factory;
1197 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(*base_cf_opt, *new_cf_opt,
1198 new_cf_opt_map));
1199 }
1200 }
1201 }
1202
1203 TEST_F(OptionsParserTest, DumpAndParse) {
1204 DBOptions base_db_opt;
1205 std::vector<ColumnFamilyOptions> base_cf_opts;
1206 std::vector<std::string> cf_names = {"default", "cf1", "cf2", "cf3",
1207 "c:f:4:4:4"
1208 "p\\i\\k\\a\\chu\\\\\\",
1209 "###rocksdb#1-testcf#2###"};
1210 const int num_cf = static_cast<int>(cf_names.size());
1211 Random rnd(302);
1212 test::RandomInitDBOptions(&base_db_opt, &rnd);
1213 base_db_opt.db_log_dir += "/#odd #but #could #happen #path #/\\\\#OMG";
1214 for (int c = 0; c < num_cf; ++c) {
1215 ColumnFamilyOptions cf_opt;
1216 Random cf_rnd(0xFB + c);
1217 test::RandomInitCFOptions(&cf_opt, &cf_rnd);
1218 if (c < 4) {
1219 cf_opt.prefix_extractor.reset(test::RandomSliceTransform(&rnd, c));
1220 }
1221 if (c < 3) {
1222 cf_opt.table_factory.reset(test::RandomTableFactory(&rnd, c));
1223 }
1224 base_cf_opts.emplace_back(cf_opt);
1225 }
1226
1227 const std::string kOptionsFileName = "test-persisted-options.ini";
1228 ASSERT_OK(PersistRocksDBOptions(base_db_opt, cf_names, base_cf_opts,
1229 kOptionsFileName, env_.get()));
1230
1231 RocksDBOptionsParser parser;
1232 ASSERT_OK(parser.Parse(kOptionsFileName, env_.get()));
1233
1234 ASSERT_OK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1235 base_db_opt, cf_names, base_cf_opts, kOptionsFileName, env_.get()));
1236
1237 ASSERT_OK(
1238 RocksDBOptionsParser::VerifyDBOptions(*parser.db_opt(), base_db_opt));
1239 for (int c = 0; c < num_cf; ++c) {
1240 const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
1241 ASSERT_NE(cf_opt, nullptr);
1242 ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
1243 base_cf_opts[c], *cf_opt, &(parser.cf_opt_maps()->at(c))));
1244 }
1245
1246 // Further verify pointer-typed options
1247 for (int c = 0; c < num_cf; ++c) {
1248 const auto* cf_opt = parser.GetCFOptions(cf_names[c]);
1249 ASSERT_NE(cf_opt, nullptr);
1250 VerifyCFPointerTypedOptions(&base_cf_opts[c], cf_opt,
1251 &(parser.cf_opt_maps()->at(c)));
1252 }
1253
1254 ASSERT_EQ(parser.GetCFOptions("does not exist"), nullptr);
1255
1256 base_db_opt.max_open_files++;
1257 ASSERT_NOK(RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1258 base_db_opt, cf_names, base_cf_opts, kOptionsFileName, env_.get()));
1259
1260 for (int c = 0; c < num_cf; ++c) {
1261 if (base_cf_opts[c].compaction_filter) {
1262 delete base_cf_opts[c].compaction_filter;
1263 }
1264 }
1265 }
1266
1267 TEST_F(OptionsParserTest, DifferentDefault) {
1268 const std::string kOptionsFileName = "test-persisted-options.ini";
1269
1270 ColumnFamilyOptions cf_level_opts;
1271 cf_level_opts.OptimizeLevelStyleCompaction();
1272
1273 ColumnFamilyOptions cf_univ_opts;
1274 cf_univ_opts.OptimizeUniversalStyleCompaction();
1275
1276 ASSERT_OK(PersistRocksDBOptions(DBOptions(), {"default", "universal"},
1277 {cf_level_opts, cf_univ_opts},
1278 kOptionsFileName, env_.get()));
1279
1280 RocksDBOptionsParser parser;
1281 ASSERT_OK(parser.Parse(kOptionsFileName, env_.get()));
1282
1283 {
1284 Options old_default_opts;
1285 old_default_opts.OldDefaults();
1286 ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
1287 ASSERT_EQ(5000, old_default_opts.max_open_files);
1288 ASSERT_EQ(-1, old_default_opts.base_background_compactions);
1289 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
1290 ASSERT_EQ(WALRecoveryMode::kTolerateCorruptedTailRecords,
1291 old_default_opts.wal_recovery_mode);
1292 }
1293 {
1294 Options old_default_opts;
1295 old_default_opts.OldDefaults(4, 6);
1296 ASSERT_EQ(10 * 1048576, old_default_opts.max_bytes_for_level_base);
1297 ASSERT_EQ(5000, old_default_opts.max_open_files);
1298 }
1299 {
1300 Options old_default_opts;
1301 old_default_opts.OldDefaults(4, 7);
1302 ASSERT_NE(10 * 1048576, old_default_opts.max_bytes_for_level_base);
1303 ASSERT_NE(4, old_default_opts.table_cache_numshardbits);
1304 ASSERT_EQ(5000, old_default_opts.max_open_files);
1305 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
1306 }
1307 {
1308 ColumnFamilyOptions old_default_cf_opts;
1309 old_default_cf_opts.OldDefaults();
1310 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
1311 ASSERT_EQ(4 << 20, old_default_cf_opts.write_buffer_size);
1312 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
1313 ASSERT_EQ(0, old_default_cf_opts.soft_pending_compaction_bytes_limit);
1314 ASSERT_EQ(0, old_default_cf_opts.hard_pending_compaction_bytes_limit);
1315 ASSERT_EQ(CompactionPri::kByCompensatedSize,
1316 old_default_cf_opts.compaction_pri);
1317 }
1318 {
1319 ColumnFamilyOptions old_default_cf_opts;
1320 old_default_cf_opts.OldDefaults(4, 6);
1321 ASSERT_EQ(2 * 1048576, old_default_cf_opts.target_file_size_base);
1322 ASSERT_EQ(CompactionPri::kByCompensatedSize,
1323 old_default_cf_opts.compaction_pri);
1324 }
1325 {
1326 ColumnFamilyOptions old_default_cf_opts;
1327 old_default_cf_opts.OldDefaults(4, 7);
1328 ASSERT_NE(2 * 1048576, old_default_cf_opts.target_file_size_base);
1329 ASSERT_EQ(CompactionPri::kByCompensatedSize,
1330 old_default_cf_opts.compaction_pri);
1331 }
1332 {
1333 Options old_default_opts;
1334 old_default_opts.OldDefaults(5, 1);
1335 ASSERT_EQ(2 * 1024U * 1024U, old_default_opts.delayed_write_rate);
1336 }
1337 {
1338 Options old_default_opts;
1339 old_default_opts.OldDefaults(5, 2);
1340 ASSERT_EQ(16 * 1024U * 1024U, old_default_opts.delayed_write_rate);
1341 }
1342
1343 Options small_opts;
1344 small_opts.OptimizeForSmallDb();
1345 ASSERT_EQ(2 << 20, small_opts.write_buffer_size);
1346 ASSERT_EQ(5000, small_opts.max_open_files);
1347 }
1348
1349 class OptionsSanityCheckTest : public OptionsParserTest {
1350 public:
1351 OptionsSanityCheckTest() {}
1352
1353 protected:
1354 Status SanityCheckCFOptions(const ColumnFamilyOptions& cf_opts,
1355 OptionsSanityCheckLevel level) {
1356 return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
1357 DBOptions(), {"default"}, {cf_opts}, kOptionsFileName, env_.get(),
1358 level);
1359 }
1360
1361 Status PersistCFOptions(const ColumnFamilyOptions& cf_opts) {
1362 Status s = env_->DeleteFile(kOptionsFileName);
1363 if (!s.ok()) {
1364 return s;
1365 }
1366 return PersistRocksDBOptions(DBOptions(), {"default"}, {cf_opts},
1367 kOptionsFileName, env_.get());
1368 }
1369
1370 const std::string kOptionsFileName = "OPTIONS";
1371 };
1372
1373 TEST_F(OptionsSanityCheckTest, SanityCheck) {
1374 ColumnFamilyOptions opts;
1375 Random rnd(301);
1376
1377 // default ColumnFamilyOptions
1378 {
1379 ASSERT_OK(PersistCFOptions(opts));
1380 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1381 }
1382
1383 // prefix_extractor
1384 {
1385 // Okay to change prefix_extractor form nullptr to non-nullptr
1386 ASSERT_EQ(opts.prefix_extractor.get(), nullptr);
1387 opts.prefix_extractor.reset(NewCappedPrefixTransform(10));
1388 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1389 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1390
1391 // persist the change
1392 ASSERT_OK(PersistCFOptions(opts));
1393 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1394
1395 // use same prefix extractor but with different parameter
1396 opts.prefix_extractor.reset(NewCappedPrefixTransform(15));
1397 // expect pass only in kSanityLevelNone
1398 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1399 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1400
1401 // repeat the test with FixedPrefixTransform
1402 opts.prefix_extractor.reset(NewFixedPrefixTransform(10));
1403 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1404 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1405
1406 // persist the change of prefix_extractor
1407 ASSERT_OK(PersistCFOptions(opts));
1408 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1409
1410 // use same prefix extractor but with different parameter
1411 opts.prefix_extractor.reset(NewFixedPrefixTransform(15));
1412 // expect pass only in kSanityLevelNone
1413 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1414 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1415
1416 // Change prefix extractor from non-nullptr to nullptr
1417 opts.prefix_extractor.reset();
1418 // expect pass as it's safe to change prefix_extractor
1419 // from non-null to null
1420 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1421 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1422 }
1423 // persist the change
1424 ASSERT_OK(PersistCFOptions(opts));
1425 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1426
1427 // table_factory
1428 {
1429 for (int tb = 0; tb <= 2; ++tb) {
1430 // change the table factory
1431 opts.table_factory.reset(test::RandomTableFactory(&rnd, tb));
1432 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1433 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1434
1435 // persist the change
1436 ASSERT_OK(PersistCFOptions(opts));
1437 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1438 }
1439 }
1440
1441 // merge_operator
1442 {
1443 for (int test = 0; test < 5; ++test) {
1444 // change the merge operator
1445 opts.merge_operator.reset(test::RandomMergeOperator(&rnd));
1446 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1447 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelNone));
1448
1449 // persist the change
1450 ASSERT_OK(PersistCFOptions(opts));
1451 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1452 }
1453 }
1454
1455 // compaction_filter
1456 {
1457 for (int test = 0; test < 5; ++test) {
1458 // change the compaction filter
1459 opts.compaction_filter = test::RandomCompactionFilter(&rnd);
1460 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1461 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1462
1463 // persist the change
1464 ASSERT_OK(PersistCFOptions(opts));
1465 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1466 delete opts.compaction_filter;
1467 opts.compaction_filter = nullptr;
1468 }
1469 }
1470
1471 // compaction_filter_factory
1472 {
1473 for (int test = 0; test < 5; ++test) {
1474 // change the compaction filter factory
1475 opts.compaction_filter_factory.reset(
1476 test::RandomCompactionFilterFactory(&rnd));
1477 ASSERT_NOK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1478 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelLooselyCompatible));
1479
1480 // persist the change
1481 ASSERT_OK(PersistCFOptions(opts));
1482 ASSERT_OK(SanityCheckCFOptions(opts, kSanityLevelExactMatch));
1483 }
1484 }
1485 }
1486
1487 namespace {
1488 bool IsEscapedString(const std::string& str) {
1489 for (size_t i = 0; i < str.size(); ++i) {
1490 if (str[i] == '\\') {
1491 // since we already handle those two consecutive '\'s in
1492 // the next if-then branch, any '\' appear at the end
1493 // of an escaped string in such case is not valid.
1494 if (i == str.size() - 1) {
1495 return false;
1496 }
1497 if (str[i + 1] == '\\') {
1498 // if there're two consecutive '\'s, skip the second one.
1499 i++;
1500 continue;
1501 }
1502 switch (str[i + 1]) {
1503 case ':':
1504 case '\\':
1505 case '#':
1506 continue;
1507 default:
1508 // if true, '\' together with str[i + 1] is not a valid escape.
1509 if (UnescapeChar(str[i + 1]) == str[i + 1]) {
1510 return false;
1511 }
1512 }
1513 } else if (isSpecialChar(str[i]) && (i == 0 || str[i - 1] != '\\')) {
1514 return false;
1515 }
1516 }
1517 return true;
1518 }
1519 } // namespace
1520
1521 TEST_F(OptionsParserTest, EscapeOptionString) {
1522 ASSERT_EQ(UnescapeOptionString(
1523 "This is a test string with \\# \\: and \\\\ escape chars."),
1524 "This is a test string with # : and \\ escape chars.");
1525
1526 ASSERT_EQ(
1527 EscapeOptionString("This is a test string with # : and \\ escape chars."),
1528 "This is a test string with \\# \\: and \\\\ escape chars.");
1529
1530 std::string readible_chars =
1531 "A String like this \"1234567890-=_)(*&^%$#@!ertyuiop[]{POIU"
1532 "YTREWQasdfghjkl;':LKJHGFDSAzxcvbnm,.?>"
1533 "<MNBVCXZ\\\" should be okay to \\#\\\\\\:\\#\\#\\#\\ "
1534 "be serialized and deserialized";
1535
1536 std::string escaped_string = EscapeOptionString(readible_chars);
1537 ASSERT_TRUE(IsEscapedString(escaped_string));
1538 // This two transformations should be canceled and should output
1539 // the original input.
1540 ASSERT_EQ(UnescapeOptionString(escaped_string), readible_chars);
1541
1542 std::string all_chars;
1543 for (unsigned char c = 0;; ++c) {
1544 all_chars += c;
1545 if (c == 255) {
1546 break;
1547 }
1548 }
1549 escaped_string = EscapeOptionString(all_chars);
1550 ASSERT_TRUE(IsEscapedString(escaped_string));
1551 ASSERT_EQ(UnescapeOptionString(escaped_string), all_chars);
1552
1553 ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
1554 " A simple statement with a comment. # like this :)"),
1555 "A simple statement with a comment.");
1556
1557 ASSERT_EQ(RocksDBOptionsParser::TrimAndRemoveComment(
1558 "Escape \\# and # comment together ."),
1559 "Escape \\# and");
1560 }
1561 #endif // !ROCKSDB_LITE
1562 } // namespace rocksdb
1563
1564 int main(int argc, char** argv) {
1565 ::testing::InitGoogleTest(&argc, argv);
1566 #ifdef GFLAGS
1567 ParseCommandLineFlags(&argc, &argv, true);
1568 #endif // GFLAGS
1569 return RUN_ALL_TESTS();
1570 }