/Users/deen/code/yugabyte-db/src/yb/rocksdb/tools/ldb_cmd.cc
Line | Count | Source (jump to first uncovered line) |
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 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | #ifndef ROCKSDB_LITE |
21 | | #include "yb/rocksdb/tools/ldb_cmd.h" |
22 | | |
23 | | #ifndef __STDC_FORMAT_MACROS |
24 | | #define __STDC_FORMAT_MACROS |
25 | | #endif |
26 | | |
27 | | #include <inttypes.h> |
28 | | |
29 | | #include <cstdlib> |
30 | | #include <ctime> |
31 | | #include <limits> |
32 | | #include <sstream> |
33 | | #include <string> |
34 | | #include <stdexcept> |
35 | | |
36 | | #include "yb/rocksdb/db/dbformat.h" |
37 | | #include "yb/rocksdb/db/db_impl.h" |
38 | | #include "yb/rocksdb/db/log_reader.h" |
39 | | #include "yb/rocksdb/db/filename.h" |
40 | | #include "yb/rocksdb/db/writebuffer.h" |
41 | | #include "yb/rocksdb/db/write_batch_internal.h" |
42 | | #include "yb/rocksdb/filter_policy.h" |
43 | | #include "yb/rocksdb/write_batch.h" |
44 | | #include "yb/rocksdb/cache.h" |
45 | | #include "yb/rocksdb/table.h" |
46 | | #include "yb/rocksdb/table_properties.h" |
47 | | #include "yb/rocksdb/table/scoped_arena_iterator.h" |
48 | | #include "yb/rocksdb/port/dirent.h" |
49 | | #include "yb/rocksdb/tools/sst_dump_tool_imp.h" |
50 | | #include "yb/rocksdb/util/coding.h" |
51 | | #include "yb/rocksdb/utilities/ttl/db_ttl_impl.h" |
52 | | |
53 | | #include "yb/util/status_log.h" |
54 | | #include "yb/util/string_util.h" |
55 | | |
56 | | namespace rocksdb { |
57 | | |
58 | | using std::string; |
59 | | |
60 | | const string LDBCommand::ARG_DB = "db"; |
61 | | const string LDBCommand::ARG_PATH = "path"; |
62 | | const string LDBCommand::ARG_HEX = "hex"; |
63 | | const string LDBCommand::ARG_KEY_HEX = "key_hex"; |
64 | | const string LDBCommand::ARG_VALUE_HEX = "value_hex"; |
65 | | const string LDBCommand::ARG_CF_NAME = "column_family"; |
66 | | const string LDBCommand::ARG_TTL = "ttl"; |
67 | | const string LDBCommand::ARG_TTL_START = "start_time"; |
68 | | const string LDBCommand::ARG_TTL_END = "end_time"; |
69 | | const string LDBCommand::ARG_TIMESTAMP = "timestamp"; |
70 | | const string LDBCommand::ARG_FROM = "from"; |
71 | | const string LDBCommand::ARG_TO = "to"; |
72 | | const string LDBCommand::ARG_MAX_KEYS = "max_keys"; |
73 | | const string LDBCommand::ARG_BLOOM_BITS = "bloom_bits"; |
74 | | const string LDBCommand::ARG_FIX_PREFIX_LEN = "fix_prefix_len"; |
75 | | const string LDBCommand::ARG_COMPRESSION_TYPE = "compression_type"; |
76 | | const string LDBCommand::ARG_BLOCK_SIZE = "block_size"; |
77 | | const string LDBCommand::ARG_AUTO_COMPACTION = "auto_compaction"; |
78 | | const string LDBCommand::ARG_DB_WRITE_BUFFER_SIZE = "db_write_buffer_size"; |
79 | | const string LDBCommand::ARG_WRITE_BUFFER_SIZE = "write_buffer_size"; |
80 | | const string LDBCommand::ARG_FILE_SIZE = "file_size"; |
81 | | const string LDBCommand::ARG_CREATE_IF_MISSING = "create_if_missing"; |
82 | | const string LDBCommand::ARG_NO_VALUE = "no_value"; |
83 | | const string LDBCommand::ARG_UNIVERSE_KEY_FILE = "key_file"; |
84 | | const string LDBCommand::ARG_ONLY_VERIFY_CHECKSUMS = "only_verify_checksums"; |
85 | | |
86 | | const char* LDBCommand::DELIM = " ==> "; |
87 | | |
88 | | namespace { |
89 | | |
90 | | void DumpWalFile(std::string wal_file, bool print_header, bool print_values, |
91 | | LDBCommandExecuteResult* exec_state); |
92 | | |
93 | | void DumpSstFile(std::string filename, bool output_hex, bool show_properties); |
94 | | }; |
95 | | |
96 | | LDBCommand* LDBCommand::InitFromCmdLineArgs( |
97 | | int argc, char** argv, const Options& options, |
98 | | const LDBOptions& ldb_options, |
99 | 4 | const std::vector<ColumnFamilyDescriptor>* column_families) { |
100 | 4 | vector<string> args; |
101 | 16 | for (int i = 1; i < argc; i++) { |
102 | 12 | args.push_back(argv[i]); |
103 | 12 | } |
104 | 4 | return InitFromCmdLineArgs(args, options, ldb_options, column_families); |
105 | 4 | } |
106 | | |
107 | | /** |
108 | | * Parse the command-line arguments and create the appropriate LDBCommand2 |
109 | | * instance. |
110 | | * The command line arguments must be in the following format: |
111 | | * ./ldb --db=PATH_TO_DB [--commonOpt1=commonOpt1Val] .. |
112 | | * COMMAND <PARAM1> <PARAM2> ... [-cmdSpecificOpt1=cmdSpecificOpt1Val] .. |
113 | | * This is similar to the command line format used by HBaseClientTool. |
114 | | * Command name is not included in args. |
115 | | * Returns nullptr if the command-line cannot be parsed. |
116 | | */ |
117 | | LDBCommand* LDBCommand::InitFromCmdLineArgs( |
118 | | const vector<string>& args, const Options& options, |
119 | | const LDBOptions& ldb_options, |
120 | 12 | const std::vector<ColumnFamilyDescriptor>* column_families) { |
121 | | // --x=y command line arguments are added as x->y map entries. |
122 | 12 | map<string, string> option_map; |
123 | | |
124 | | // Command-line arguments of the form --hex end up in this array as hex |
125 | 12 | vector<string> flags; |
126 | | |
127 | | // Everything other than option_map and flags. Represents commands |
128 | | // and their parameters. For eg: put key1 value1 go into this vector. |
129 | 12 | vector<string> cmdTokens; |
130 | | |
131 | 12 | const string OPTION_PREFIX = "--"; |
132 | | |
133 | 36 | for (const auto& arg : args) { |
134 | 36 | if (arg[0] == '-' && arg[1] == '-') { |
135 | 24 | auto pos = arg.find('=', 0); |
136 | 24 | if (pos != string::npos) { |
137 | 22 | string optionKey = arg.substr(OPTION_PREFIX.size(), pos - OPTION_PREFIX.size()); |
138 | 22 | option_map[optionKey] = arg.substr(pos + 1); |
139 | 2 | } else { |
140 | 2 | string optionKey = arg.substr(OPTION_PREFIX.size()); |
141 | 2 | flags.push_back(optionKey); |
142 | 2 | } |
143 | 12 | } else { |
144 | 12 | cmdTokens.push_back(arg); |
145 | 12 | } |
146 | 36 | } |
147 | | |
148 | 12 | if (cmdTokens.size() < 1) { |
149 | 0 | fprintf(stderr, "Command not specified!"); |
150 | 0 | return nullptr; |
151 | 0 | } |
152 | | |
153 | 12 | string cmd = cmdTokens[0]; |
154 | 12 | vector<string> cmdParams(cmdTokens.begin()+1, cmdTokens.end()); |
155 | 12 | LDBCommand* command = LDBCommand::SelectCommand( |
156 | 12 | cmd, |
157 | 12 | cmdParams, |
158 | 12 | option_map, |
159 | 12 | flags |
160 | 12 | ); |
161 | | |
162 | 12 | if (command) { |
163 | 12 | command->SetDBOptions(options); |
164 | 12 | command->SetLDBOptions(ldb_options); |
165 | 12 | } |
166 | 12 | return command; |
167 | 12 | } |
168 | | |
169 | | LDBCommand* LDBCommand::SelectCommand( |
170 | | const std::string& cmd, |
171 | | const vector<string>& cmdParams, |
172 | | const map<string, string>& option_map, |
173 | | const vector<string>& flags |
174 | 12 | ) { |
175 | | |
176 | 12 | if (cmd == GetCommand::Name()) { |
177 | 0 | return new GetCommand(cmdParams, option_map, flags); |
178 | 12 | } else if (cmd == PutCommand::Name()) { |
179 | 0 | return new PutCommand(cmdParams, option_map, flags); |
180 | 12 | } else if (cmd == BatchPutCommand::Name()) { |
181 | 0 | return new BatchPutCommand(cmdParams, option_map, flags); |
182 | 12 | } else if (cmd == ScanCommand::Name()) { |
183 | 2 | return new ScanCommand(cmdParams, option_map, flags); |
184 | 10 | } else if (cmd == DeleteCommand::Name()) { |
185 | 0 | return new DeleteCommand(cmdParams, option_map, flags); |
186 | 10 | } else if (cmd == ApproxSizeCommand::Name()) { |
187 | 0 | return new ApproxSizeCommand(cmdParams, option_map, flags); |
188 | 10 | } else if (cmd == DBQuerierCommand::Name()) { |
189 | 0 | return new DBQuerierCommand(cmdParams, option_map, flags); |
190 | 10 | } else if (cmd == CompactorCommand::Name()) { |
191 | 0 | return new CompactorCommand(cmdParams, option_map, flags); |
192 | 10 | } else if (cmd == WALDumperCommand::Name()) { |
193 | 0 | return new WALDumperCommand(cmdParams, option_map, flags); |
194 | 10 | } else if (cmd == ReduceDBLevelsCommand::Name()) { |
195 | 8 | return new ReduceDBLevelsCommand(cmdParams, option_map, flags); |
196 | 2 | } else if (cmd == ChangeCompactionStyleCommand::Name()) { |
197 | 0 | return new ChangeCompactionStyleCommand(cmdParams, option_map, flags); |
198 | 2 | } else if (cmd == DBDumperCommand::Name()) { |
199 | 0 | return new DBDumperCommand(cmdParams, option_map, flags); |
200 | 2 | } else if (cmd == DBLoaderCommand::Name()) { |
201 | 0 | return new DBLoaderCommand(cmdParams, option_map, flags); |
202 | 2 | } else if (cmd == ManifestDumpCommand::Name()) { |
203 | 0 | return new ManifestDumpCommand(cmdParams, option_map, flags); |
204 | 2 | } else if (cmd == ListColumnFamiliesCommand::Name()) { |
205 | 0 | return new ListColumnFamiliesCommand(cmdParams, option_map, flags); |
206 | 2 | } else if (cmd == CreateColumnFamilyCommand::Name()) { |
207 | 0 | return new CreateColumnFamilyCommand(cmdParams, option_map, flags); |
208 | 2 | } else if (cmd == DBFileDumperCommand::Name()) { |
209 | 0 | return new DBFileDumperCommand(cmdParams, option_map, flags); |
210 | 2 | } else if (cmd == InternalDumpCommand::Name()) { |
211 | 0 | return new InternalDumpCommand(cmdParams, option_map, flags); |
212 | 2 | } else if (cmd == CheckConsistencyCommand::Name()) { |
213 | 2 | return new CheckConsistencyCommand(cmdParams, option_map, flags); |
214 | 2 | } |
215 | 0 | return nullptr; |
216 | 0 | } |
217 | | |
218 | | |
219 | | /** |
220 | | * Parses the specific integer option and fills in the value. |
221 | | * Returns true if the option is found. |
222 | | * Returns false if the option is not found or if there is an error parsing the |
223 | | * value. If there is an error, the specified exec_state is also |
224 | | * updated. |
225 | | */ |
226 | | bool LDBCommand::ParseIntOption(const map<string, string>& options, |
227 | | const string& option, int& value, |
228 | 114 | LDBCommandExecuteResult& exec_state) { |
229 | | |
230 | 114 | map<string, string>::const_iterator itr = option_map_.find(option); |
231 | 114 | if (itr != option_map_.end()) { |
232 | 8 | try { |
233 | | #if defined(CYGWIN) |
234 | | value = strtol(itr->second.c_str(), 0, 10); |
235 | | #else |
236 | 8 | value = stoi(itr->second); |
237 | 8 | #endif |
238 | 8 | return true; |
239 | 0 | } catch(const std::invalid_argument&) { |
240 | 0 | exec_state = |
241 | 0 | LDBCommandExecuteResult::Failed(option + " has an invalid value."); |
242 | 0 | } catch(const std::out_of_range&) { |
243 | 0 | exec_state = LDBCommandExecuteResult::Failed( |
244 | 0 | option + " has a value out-of-range."); |
245 | 0 | } |
246 | 8 | } |
247 | 106 | return false; |
248 | 114 | } |
249 | | |
250 | | /** |
251 | | * Parses the specified option and fills in the value. |
252 | | * Returns true if the option is found. |
253 | | * Returns false otherwise. |
254 | | */ |
255 | | bool LDBCommand::ParseStringOption(const map<string, string>& options, |
256 | 0 | const string& option, string* value) { |
257 | 0 | auto itr = option_map_.find(option); |
258 | 0 | if (itr != option_map_.end()) { |
259 | 0 | *value = itr->second; |
260 | 0 | return true; |
261 | 0 | } |
262 | 0 | return false; |
263 | 0 | } |
264 | | |
265 | 17 | Options LDBCommand::PrepareOptionsForOpenDB() { |
266 | 17 | Options opt = options_; |
267 | 17 | opt.create_if_missing = false; |
268 | | |
269 | 17 | map<string, string>::const_iterator itr; |
270 | | |
271 | 17 | BlockBasedTableOptions table_options; |
272 | 17 | bool use_table_options = false; |
273 | 17 | int bits; |
274 | 17 | if (ParseIntOption(option_map_, ARG_BLOOM_BITS, bits, exec_state_)) { |
275 | 0 | if (bits > 0) { |
276 | 0 | use_table_options = true; |
277 | 0 | table_options.filter_policy.reset(NewBloomFilterPolicy(bits)); |
278 | 0 | } else { |
279 | 0 | exec_state_ = |
280 | 0 | LDBCommandExecuteResult::Failed(ARG_BLOOM_BITS + " must be > 0."); |
281 | 0 | } |
282 | 0 | } |
283 | | |
284 | 17 | int block_size; |
285 | 17 | if (ParseIntOption(option_map_, ARG_BLOCK_SIZE, block_size, exec_state_)) { |
286 | 0 | if (block_size > 0) { |
287 | 0 | use_table_options = true; |
288 | 0 | table_options.block_size = block_size; |
289 | 0 | } else { |
290 | 0 | exec_state_ = |
291 | 0 | LDBCommandExecuteResult::Failed(ARG_BLOCK_SIZE + " must be > 0."); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | 17 | if (use_table_options) { |
296 | 0 | opt.table_factory.reset(NewBlockBasedTableFactory(table_options)); |
297 | 0 | } |
298 | | |
299 | 17 | itr = option_map_.find(ARG_AUTO_COMPACTION); |
300 | 17 | if (itr != option_map_.end()) { |
301 | 0 | opt.disable_auto_compactions = !StringToBool(itr->second); |
302 | 0 | } |
303 | | |
304 | 17 | itr = option_map_.find(ARG_COMPRESSION_TYPE); |
305 | 17 | if (itr != option_map_.end()) { |
306 | 0 | string comp = itr->second; |
307 | 0 | if (comp == "no") { |
308 | 0 | opt.compression = kNoCompression; |
309 | 0 | } else if (comp == "snappy") { |
310 | 0 | opt.compression = kSnappyCompression; |
311 | 0 | } else if (comp == "zlib") { |
312 | 0 | opt.compression = kZlibCompression; |
313 | 0 | } else if (comp == "bzip2") { |
314 | 0 | opt.compression = kBZip2Compression; |
315 | 0 | } else if (comp == "lz4") { |
316 | 0 | opt.compression = kLZ4Compression; |
317 | 0 | } else if (comp == "lz4hc") { |
318 | 0 | opt.compression = kLZ4HCCompression; |
319 | 0 | } else if (comp == "zstd") { |
320 | 0 | opt.compression = kZSTDNotFinalCompression; |
321 | 0 | } else { |
322 | | // Unknown compression. |
323 | 0 | exec_state_ = |
324 | 0 | LDBCommandExecuteResult::Failed("Unknown compression level: " + comp); |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | 17 | int db_write_buffer_size; |
329 | 17 | if (ParseIntOption(option_map_, ARG_DB_WRITE_BUFFER_SIZE, |
330 | 0 | db_write_buffer_size, exec_state_)) { |
331 | 0 | if (db_write_buffer_size >= 0) { |
332 | 0 | opt.db_write_buffer_size = db_write_buffer_size; |
333 | 0 | } else { |
334 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(ARG_DB_WRITE_BUFFER_SIZE + |
335 | 0 | " must be >= 0."); |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | 17 | int write_buffer_size; |
340 | 17 | if (ParseIntOption(option_map_, ARG_WRITE_BUFFER_SIZE, write_buffer_size, |
341 | 0 | exec_state_)) { |
342 | 0 | if (write_buffer_size > 0) { |
343 | 0 | opt.write_buffer_size = write_buffer_size; |
344 | 0 | } else { |
345 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(ARG_WRITE_BUFFER_SIZE + |
346 | 0 | " must be > 0."); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | 17 | int file_size; |
351 | 17 | if (ParseIntOption(option_map_, ARG_FILE_SIZE, file_size, exec_state_)) { |
352 | 0 | if (file_size > 0) { |
353 | 0 | opt.target_file_size_base = file_size; |
354 | 0 | } else { |
355 | 0 | exec_state_ = |
356 | 0 | LDBCommandExecuteResult::Failed(ARG_FILE_SIZE + " must be > 0."); |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 17 | if (opt.db_paths.size() == 0) { |
361 | 17 | opt.db_paths.emplace_back(db_path_, std::numeric_limits<uint64_t>::max()); |
362 | 17 | } |
363 | | |
364 | 17 | int fix_prefix_len; |
365 | 17 | if (ParseIntOption(option_map_, ARG_FIX_PREFIX_LEN, fix_prefix_len, |
366 | 0 | exec_state_)) { |
367 | 0 | if (fix_prefix_len > 0) { |
368 | 0 | opt.prefix_extractor.reset( |
369 | 0 | NewFixedPrefixTransform(static_cast<size_t>(fix_prefix_len))); |
370 | 0 | } else { |
371 | 0 | exec_state_ = |
372 | 0 | LDBCommandExecuteResult::Failed(ARG_FIX_PREFIX_LEN + " must be > 0."); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 15 | opt.env = env_ ? env_.get() : Env::Default(); |
377 | 17 | opt.checkpoint_env = Env::Default(); |
378 | | |
379 | 17 | return opt; |
380 | 17 | } |
381 | | |
382 | | bool LDBCommand::ParseKeyValue(const string& line, string* key, string* value, |
383 | 0 | bool is_key_hex, bool is_value_hex) { |
384 | 0 | size_t pos = line.find(DELIM); |
385 | 0 | if (pos != string::npos) { |
386 | 0 | *key = line.substr(0, pos); |
387 | 0 | *value = line.substr(pos + strlen(DELIM)); |
388 | 0 | if (is_key_hex) { |
389 | 0 | *key = HexToString(*key); |
390 | 0 | } |
391 | 0 | if (is_value_hex) { |
392 | 0 | *value = HexToString(*value); |
393 | 0 | } |
394 | 0 | return true; |
395 | 0 | } else { |
396 | 0 | return false; |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | | /** |
401 | | * Make sure that ONLY the command-line options and flags expected by this |
402 | | * command are specified on the command-line. Extraneous options are usually |
403 | | * the result of user error. |
404 | | * Returns true if all checks pass. Else returns false, and prints an |
405 | | * appropriate error msg to stderr. |
406 | | */ |
407 | 4 | bool LDBCommand::ValidateCmdLineOptions() { |
408 | | |
409 | 4 | for (map<string, string>::const_iterator itr = option_map_.begin(); |
410 | 10 | itr != option_map_.end(); ++itr) { |
411 | 6 | if (find(valid_cmd_line_options_.begin(), |
412 | 6 | valid_cmd_line_options_.end(), itr->first) == |
413 | 0 | valid_cmd_line_options_.end()) { |
414 | 0 | fprintf(stderr, "Invalid command-line option %s\n", itr->first.c_str()); |
415 | 0 | return false; |
416 | 0 | } |
417 | 6 | } |
418 | | |
419 | 4 | for (vector<string>::const_iterator itr = flags_.begin(); |
420 | 6 | itr != flags_.end(); ++itr) { |
421 | 2 | if (find(valid_cmd_line_options_.begin(), |
422 | 2 | valid_cmd_line_options_.end(), *itr) == |
423 | 0 | valid_cmd_line_options_.end()) { |
424 | 0 | fprintf(stderr, "Invalid command-line flag %s\n", itr->c_str()); |
425 | 0 | return false; |
426 | 0 | } |
427 | 2 | } |
428 | | |
429 | 4 | if (!NoDBOpen() && option_map_.find(ARG_DB) == option_map_.end() && |
430 | 0 | option_map_.find(ARG_PATH) == option_map_.end()) { |
431 | 0 | fprintf(stderr, "Either %s or %s must be specified.\n", ARG_DB.c_str(), |
432 | 0 | ARG_PATH.c_str()); |
433 | 0 | return false; |
434 | 0 | } |
435 | | |
436 | 4 | return true; |
437 | 4 | } |
438 | | |
439 | | CompactorCommand::CompactorCommand(const vector<string>& params, |
440 | | const map<string, string>& options, const vector<string>& flags) : |
441 | | LDBCommand(options, flags, false, |
442 | | BuildCmdLineOptions({ARG_FROM, ARG_TO, ARG_HEX, ARG_KEY_HEX, |
443 | | ARG_VALUE_HEX, ARG_TTL})), |
444 | 0 | null_from_(true), null_to_(true) { |
445 | |
|
446 | 0 | map<string, string>::const_iterator itr = options.find(ARG_FROM); |
447 | 0 | if (itr != options.end()) { |
448 | 0 | null_from_ = false; |
449 | 0 | from_ = itr->second; |
450 | 0 | } |
451 | |
|
452 | 0 | itr = options.find(ARG_TO); |
453 | 0 | if (itr != options.end()) { |
454 | 0 | null_to_ = false; |
455 | 0 | to_ = itr->second; |
456 | 0 | } |
457 | |
|
458 | 0 | if (is_key_hex_) { |
459 | 0 | if (!null_from_) { |
460 | 0 | from_ = HexToString(from_); |
461 | 0 | } |
462 | 0 | if (!null_to_) { |
463 | 0 | to_ = HexToString(to_); |
464 | 0 | } |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | 0 | void CompactorCommand::Help(string& ret) { |
469 | 0 | ret.append(" "); |
470 | 0 | ret.append(CompactorCommand::Name()); |
471 | 0 | ret.append(HelpRangeCmdArgs()); |
472 | 0 | ret.append("\n"); |
473 | 0 | } |
474 | | |
475 | 0 | void CompactorCommand::DoCommand() { |
476 | 0 | if (!db_) { |
477 | 0 | assert(GetExecuteState().IsFailed()); |
478 | 0 | return; |
479 | 0 | } |
480 | | |
481 | 0 | Slice* begin = nullptr; |
482 | 0 | Slice* end = nullptr; |
483 | 0 | if (!null_from_) { |
484 | 0 | begin = new Slice(from_); |
485 | 0 | } |
486 | 0 | if (!null_to_) { |
487 | 0 | end = new Slice(to_); |
488 | 0 | } |
489 | |
|
490 | 0 | CompactRangeOptions cro; |
491 | 0 | cro.bottommost_level_compaction = BottommostLevelCompaction::kForce; |
492 | |
|
493 | 0 | CHECK_OK(db_->CompactRange(cro, begin, end)); |
494 | 0 | exec_state_ = LDBCommandExecuteResult::Succeed(""); |
495 | |
|
496 | 0 | delete begin; |
497 | 0 | delete end; |
498 | 0 | } |
499 | | |
500 | | // ---------------------------------------------------------------------------- |
501 | | |
502 | | const string DBLoaderCommand::ARG_DISABLE_WAL = "disable_wal"; |
503 | | const string DBLoaderCommand::ARG_BULK_LOAD = "bulk_load"; |
504 | | const string DBLoaderCommand::ARG_COMPACT = "compact"; |
505 | | |
506 | | DBLoaderCommand::DBLoaderCommand(const vector<string>& params, |
507 | | const map<string, string>& options, const vector<string>& flags) : |
508 | | LDBCommand(options, flags, false, |
509 | | BuildCmdLineOptions({ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, |
510 | | ARG_FROM, ARG_TO, ARG_CREATE_IF_MISSING, |
511 | | ARG_DISABLE_WAL, ARG_BULK_LOAD, |
512 | | ARG_COMPACT})), |
513 | | create_if_missing_(false), disable_wal_(false), bulk_load_(false), |
514 | 0 | compact_(false) { |
515 | |
|
516 | 0 | create_if_missing_ = IsFlagPresent(flags, ARG_CREATE_IF_MISSING); |
517 | 0 | disable_wal_ = IsFlagPresent(flags, ARG_DISABLE_WAL); |
518 | 0 | bulk_load_ = IsFlagPresent(flags, ARG_BULK_LOAD); |
519 | 0 | compact_ = IsFlagPresent(flags, ARG_COMPACT); |
520 | 0 | } |
521 | | |
522 | 0 | void DBLoaderCommand::Help(string& ret) { |
523 | 0 | ret.append(" "); |
524 | 0 | ret.append(DBLoaderCommand::Name()); |
525 | 0 | ret.append(" [--" + ARG_CREATE_IF_MISSING + "]"); |
526 | 0 | ret.append(" [--" + ARG_DISABLE_WAL + "]"); |
527 | 0 | ret.append(" [--" + ARG_BULK_LOAD + "]"); |
528 | 0 | ret.append(" [--" + ARG_COMPACT + "]"); |
529 | 0 | ret.append("\n"); |
530 | 0 | } |
531 | | |
532 | 0 | Options DBLoaderCommand::PrepareOptionsForOpenDB() { |
533 | 0 | Options opt = LDBCommand::PrepareOptionsForOpenDB(); |
534 | 0 | opt.create_if_missing = create_if_missing_; |
535 | 0 | if (bulk_load_) { |
536 | 0 | opt.PrepareForBulkLoad(); |
537 | 0 | } |
538 | 0 | return opt; |
539 | 0 | } |
540 | | |
541 | 0 | void DBLoaderCommand::DoCommand() { |
542 | 0 | if (!db_) { |
543 | 0 | assert(GetExecuteState().IsFailed()); |
544 | 0 | return; |
545 | 0 | } |
546 | | |
547 | 0 | WriteOptions write_options; |
548 | 0 | if (disable_wal_) { |
549 | 0 | write_options.disableWAL = true; |
550 | 0 | } |
551 | |
|
552 | 0 | int bad_lines = 0; |
553 | 0 | string line; |
554 | 0 | while (getline(std::cin, line, '\n')) { |
555 | 0 | string key; |
556 | 0 | string value; |
557 | 0 | if (ParseKeyValue(line, &key, &value, is_key_hex_, is_value_hex_)) { |
558 | 0 | CHECK_OK(db_->Put(write_options, GetCfHandle(), Slice(key), Slice(value))); |
559 | 0 | } else if (0 == line.find("Keys in range:")) { |
560 | | // ignore this line |
561 | 0 | } else if (0 == line.find("Created bg thread 0x")) { |
562 | | // ignore this line |
563 | 0 | } else { |
564 | 0 | bad_lines++; |
565 | 0 | } |
566 | 0 | } |
567 | |
|
568 | 0 | if (bad_lines > 0) { |
569 | 0 | std::cout << "Warning: " << bad_lines << " bad lines ignored." << std::endl; |
570 | 0 | } |
571 | 0 | if (compact_) { |
572 | 0 | CHECK_OK(db_->CompactRange(CompactRangeOptions(), GetCfHandle(), nullptr, nullptr)); |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | | // ---------------------------------------------------------------------------- |
577 | | |
578 | | namespace { |
579 | | |
580 | 0 | void DumpManifestFile(std::string file, bool verbose, bool hex) { |
581 | 0 | Options options; |
582 | 0 | EnvOptions sopt; |
583 | 0 | std::string dbname("dummy"); |
584 | 0 | std::shared_ptr<Cache> tc(NewLRUCache(options.max_open_files - 10, |
585 | 0 | options.table_cache_numshardbits)); |
586 | | // Notice we are using the default options not through SanitizeOptions(), |
587 | | // if VersionSet::DumpManifest() depends on any option done by |
588 | | // SanitizeOptions(), we need to initialize it manually. |
589 | 0 | options.db_paths.emplace_back("dummy", 0); |
590 | 0 | options.num_levels = 64; |
591 | 0 | WriteController wc(options.delayed_write_rate); |
592 | 0 | WriteBuffer wb(options.db_write_buffer_size); |
593 | 0 | VersionSet versions(dbname, &options, sopt, tc.get(), &wb, &wc); |
594 | 0 | Status s = versions.DumpManifest(options, file, verbose, hex); |
595 | 0 | if (!s.ok()) { |
596 | 0 | printf("Error in processing file %s %s\n", file.c_str(), |
597 | 0 | s.ToString().c_str()); |
598 | 0 | } |
599 | 0 | } |
600 | | |
601 | | } // namespace |
602 | | |
603 | | const string ManifestDumpCommand::ARG_VERBOSE = "verbose"; |
604 | | const string ManifestDumpCommand::ARG_JSON = "json"; |
605 | | const string ManifestDumpCommand::ARG_PATH = "path"; |
606 | | |
607 | 0 | void ManifestDumpCommand::Help(string& ret) { |
608 | 0 | ret.append(" "); |
609 | 0 | ret.append(ManifestDumpCommand::Name()); |
610 | 0 | ret.append(" [--" + ARG_VERBOSE + "]"); |
611 | 0 | ret.append(" [--" + ARG_JSON + "]"); |
612 | 0 | ret.append(" [--" + ARG_PATH + "=<path_to_manifest_file>]"); |
613 | 0 | ret.append("\n"); |
614 | 0 | } |
615 | | |
616 | | ManifestDumpCommand::ManifestDumpCommand(const vector<string>& params, |
617 | | const map<string, string>& options, const vector<string>& flags) : |
618 | | LDBCommand(options, flags, false, |
619 | | BuildCmdLineOptions({ARG_VERBOSE, ARG_PATH, ARG_HEX, ARG_JSON})), |
620 | | verbose_(false), |
621 | 0 | path_("") { |
622 | 0 | verbose_ = IsFlagPresent(flags, ARG_VERBOSE); |
623 | |
|
624 | 0 | map<string, string>::const_iterator itr = options.find(ARG_PATH); |
625 | 0 | if (itr != options.end()) { |
626 | 0 | path_ = itr->second; |
627 | 0 | if (path_.empty()) { |
628 | 0 | exec_state_ = LDBCommandExecuteResult::Failed("--path: missing pathname"); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 0 | void ManifestDumpCommand::DoCommand() { |
634 | |
|
635 | 0 | std::string manifestfile; |
636 | |
|
637 | 0 | if (!path_.empty()) { |
638 | 0 | manifestfile = path_; |
639 | 0 | } else { |
640 | 0 | bool found = false; |
641 | | // We need to find the manifest file by searching the directory |
642 | | // containing the db for files of the form MANIFEST_[0-9]+ |
643 | |
|
644 | 0 | auto CloseDir = [](DIR* p) { closedir(p); }; |
645 | 0 | std::unique_ptr<DIR, decltype(CloseDir)> d(opendir(db_path_.c_str()), |
646 | 0 | CloseDir); |
647 | |
|
648 | 0 | if (d == nullptr) { |
649 | 0 | exec_state_ = |
650 | 0 | LDBCommandExecuteResult::Failed(db_path_ + " is not a directory"); |
651 | 0 | return; |
652 | 0 | } |
653 | 0 | struct dirent* entry; |
654 | 0 | while ((entry = readdir(d.get())) != nullptr) { |
655 | 0 | unsigned int match; |
656 | 0 | uint64_t num; |
657 | 0 | if (sscanf(entry->d_name, "MANIFEST-%" PRIu64 "%n", &num, &match) && |
658 | 0 | match == strlen(entry->d_name)) { |
659 | 0 | if (!found) { |
660 | 0 | manifestfile = db_path_ + "/" + std::string(entry->d_name); |
661 | 0 | found = true; |
662 | 0 | } else { |
663 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
664 | 0 | "Multiple MANIFEST files found; use --path to select one"); |
665 | 0 | return; |
666 | 0 | } |
667 | 0 | } |
668 | 0 | } |
669 | 0 | } |
670 | |
|
671 | 0 | if (verbose_) { |
672 | 0 | printf("Processing Manifest file %s\n", manifestfile.c_str()); |
673 | 0 | } |
674 | |
|
675 | 0 | DumpManifestFile(manifestfile, verbose_, is_key_hex_); |
676 | |
|
677 | 0 | if (verbose_) { |
678 | 0 | printf("Processing Manifest file %s done\n", manifestfile.c_str()); |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | | // ---------------------------------------------------------------------------- |
683 | | |
684 | 0 | void ListColumnFamiliesCommand::Help(string& ret) { |
685 | 0 | ret.append(" "); |
686 | 0 | ret.append(ListColumnFamiliesCommand::Name()); |
687 | 0 | ret.append(" full_path_to_db_directory "); |
688 | 0 | ret.append("\n"); |
689 | 0 | } |
690 | | |
691 | | ListColumnFamiliesCommand::ListColumnFamiliesCommand( |
692 | | const vector<string>& params, const map<string, string>& options, |
693 | | const vector<string>& flags) |
694 | 0 | : LDBCommand(options, flags, false, {}) { |
695 | |
|
696 | 0 | if (params.size() != 1) { |
697 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
698 | 0 | "dbname must be specified for the list_column_families command"); |
699 | 0 | } else { |
700 | 0 | dbname_ = params[0]; |
701 | 0 | } |
702 | 0 | } |
703 | | |
704 | 0 | void ListColumnFamiliesCommand::DoCommand() { |
705 | 0 | vector<string> column_families; |
706 | 0 | Status s = DB::ListColumnFamilies(DBOptions(), dbname_, &column_families); |
707 | 0 | if (!s.ok()) { |
708 | 0 | printf("Error in processing db %s %s\n", dbname_.c_str(), |
709 | 0 | s.ToString().c_str()); |
710 | 0 | } else { |
711 | 0 | printf("Column families in %s: \n{", dbname_.c_str()); |
712 | 0 | bool first = true; |
713 | 0 | for (auto cf : column_families) { |
714 | 0 | if (!first) { |
715 | 0 | printf(", "); |
716 | 0 | } |
717 | 0 | first = false; |
718 | 0 | printf("%s", cf.c_str()); |
719 | 0 | } |
720 | 0 | printf("}\n"); |
721 | 0 | } |
722 | 0 | } |
723 | | |
724 | 0 | void CreateColumnFamilyCommand::Help(string& ret) { |
725 | 0 | ret.append(" "); |
726 | 0 | ret.append(CreateColumnFamilyCommand::Name()); |
727 | 0 | ret.append(" --db=<db_path> <new_column_family_name>"); |
728 | 0 | ret.append("\n"); |
729 | 0 | } |
730 | | |
731 | | CreateColumnFamilyCommand::CreateColumnFamilyCommand( |
732 | | const vector<string>& params, const map<string, string>& options, |
733 | | const vector<string>& flags) |
734 | 0 | : LDBCommand(options, flags, true, {ARG_DB}) { |
735 | 0 | if (params.size() != 1) { |
736 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
737 | 0 | "new column family name must be specified"); |
738 | 0 | } else { |
739 | 0 | new_cf_name_ = params[0]; |
740 | 0 | } |
741 | 0 | } |
742 | | |
743 | 0 | void CreateColumnFamilyCommand::DoCommand() { |
744 | 0 | ColumnFamilyHandle* new_cf_handle; |
745 | 0 | Status st = db_->CreateColumnFamily(options_, new_cf_name_, &new_cf_handle); |
746 | 0 | if (st.ok()) { |
747 | 0 | fprintf(stdout, "OK\n"); |
748 | 0 | } else { |
749 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
750 | 0 | "Fail to create new column family: " + st.ToString()); |
751 | 0 | } |
752 | 0 | delete new_cf_handle; |
753 | 0 | CloseDB(); |
754 | 0 | } |
755 | | |
756 | | // ---------------------------------------------------------------------------- |
757 | | |
758 | | namespace { |
759 | | |
760 | 0 | string ReadableTime(int unixtime) { |
761 | 0 | char time_buffer[80]; |
762 | 0 | time_t rawtime = unixtime; |
763 | 0 | struct tm tInfo; |
764 | 0 | struct tm* timeinfo = localtime_r(&rawtime, &tInfo); |
765 | 0 | assert(timeinfo == &tInfo); |
766 | 0 | strftime(time_buffer, 80, "%c", timeinfo); |
767 | 0 | return string(time_buffer); |
768 | 0 | } |
769 | | |
770 | | // This function only called when it's the sane case of >1 buckets in time-range |
771 | | // Also called only when timekv falls between ttl_start and ttl_end provided |
772 | | void IncBucketCounts(vector<uint64_t>* bucket_counts, int ttl_start, |
773 | 0 | int time_range, int bucket_size, int timekv, int num_buckets) { |
774 | 0 | assert(time_range > 0 && timekv >= ttl_start && bucket_size > 0 && |
775 | 0 | timekv < (ttl_start + time_range) && num_buckets > 1); |
776 | 0 | int bucket = (timekv - ttl_start) / bucket_size; |
777 | 0 | (*bucket_counts)[bucket]++; |
778 | 0 | } |
779 | | |
780 | | void PrintBucketCounts(const vector<uint64_t>& bucket_counts, int ttl_start, |
781 | 0 | int ttl_end, int bucket_size, int num_buckets) { |
782 | 0 | int time_point = ttl_start; |
783 | 0 | for(int i = 0; i < num_buckets - 1; i++, time_point += bucket_size) { |
784 | 0 | fprintf(stdout, "Keys in range %s to %s : %" PRIu64 "\n", |
785 | 0 | ReadableTime(time_point).c_str(), |
786 | 0 | ReadableTime(time_point + bucket_size).c_str(), |
787 | 0 | bucket_counts[i]); |
788 | 0 | } |
789 | 0 | fprintf(stdout, "Keys in range %s to %s : %" PRIu64 "\n", |
790 | 0 | ReadableTime(time_point).c_str(), |
791 | 0 | ReadableTime(ttl_end).c_str(), |
792 | 0 | bucket_counts[num_buckets - 1]); |
793 | 0 | } |
794 | | |
795 | | } // namespace |
796 | | |
797 | | const string InternalDumpCommand::ARG_COUNT_ONLY = "count_only"; |
798 | | const string InternalDumpCommand::ARG_COUNT_DELIM = "count_delim"; |
799 | | const string InternalDumpCommand::ARG_STATS = "stats"; |
800 | | const string InternalDumpCommand::ARG_INPUT_KEY_HEX = "input_key_hex"; |
801 | | |
802 | | InternalDumpCommand::InternalDumpCommand(const vector<string>& params, |
803 | | const map<string, string>& options, |
804 | | const vector<string>& flags) |
805 | | : LDBCommand( |
806 | | options, flags, true, |
807 | | BuildCmdLineOptions({ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, ARG_FROM, |
808 | | ARG_TO, ARG_MAX_KEYS, ARG_COUNT_ONLY, |
809 | | ARG_COUNT_DELIM, ARG_STATS, ARG_INPUT_KEY_HEX})), |
810 | | has_from_(false), |
811 | | has_to_(false), |
812 | | max_keys_(-1), |
813 | | delim_("."), |
814 | | count_only_(false), |
815 | | count_delim_(false), |
816 | | print_stats_(false), |
817 | 0 | is_input_key_hex_(false) { |
818 | 0 | has_from_ = ParseStringOption(options, ARG_FROM, &from_); |
819 | 0 | has_to_ = ParseStringOption(options, ARG_TO, &to_); |
820 | |
|
821 | 0 | ParseIntOption(options, ARG_MAX_KEYS, max_keys_, exec_state_); |
822 | 0 | map<string, string>::const_iterator itr = options.find(ARG_COUNT_DELIM); |
823 | 0 | if (itr != options.end()) { |
824 | 0 | delim_ = itr->second; |
825 | 0 | count_delim_ = true; |
826 | | // fprintf(stdout,"delim = %c\n",delim_[0]); |
827 | 0 | } else { |
828 | 0 | count_delim_ = IsFlagPresent(flags, ARG_COUNT_DELIM); |
829 | 0 | delim_ = "."; |
830 | 0 | } |
831 | |
|
832 | 0 | print_stats_ = IsFlagPresent(flags, ARG_STATS); |
833 | 0 | count_only_ = IsFlagPresent(flags, ARG_COUNT_ONLY); |
834 | 0 | is_input_key_hex_ = IsFlagPresent(flags, ARG_INPUT_KEY_HEX); |
835 | |
|
836 | 0 | if (is_input_key_hex_) { |
837 | 0 | if (has_from_) { |
838 | 0 | from_ = HexToString(from_); |
839 | 0 | } |
840 | 0 | if (has_to_) { |
841 | 0 | to_ = HexToString(to_); |
842 | 0 | } |
843 | 0 | } |
844 | 0 | } |
845 | | |
846 | 0 | void InternalDumpCommand::Help(string& ret) { |
847 | 0 | ret.append(" "); |
848 | 0 | ret.append(InternalDumpCommand::Name()); |
849 | 0 | ret.append(HelpRangeCmdArgs()); |
850 | 0 | ret.append(" [--" + ARG_INPUT_KEY_HEX + "]"); |
851 | 0 | ret.append(" [--" + ARG_MAX_KEYS + "=<N>]"); |
852 | 0 | ret.append(" [--" + ARG_COUNT_ONLY + "]"); |
853 | 0 | ret.append(" [--" + ARG_COUNT_DELIM + "=<char>]"); |
854 | 0 | ret.append(" [--" + ARG_STATS + "]"); |
855 | 0 | ret.append("\n"); |
856 | 0 | } |
857 | | |
858 | 0 | void InternalDumpCommand::DoCommand() { |
859 | 0 | if (!db_) { |
860 | 0 | assert(GetExecuteState().IsFailed()); |
861 | 0 | return; |
862 | 0 | } |
863 | | |
864 | 0 | if (print_stats_) { |
865 | 0 | string stats; |
866 | 0 | if (db_->GetProperty(GetCfHandle(), "rocksdb.stats", &stats)) { |
867 | 0 | fprintf(stdout, "%s\n", stats.c_str()); |
868 | 0 | } |
869 | 0 | } |
870 | | |
871 | | // Cast as DBImpl to get internal iterator |
872 | 0 | DBImpl* idb = dynamic_cast<DBImpl*>(db_); |
873 | 0 | if (!idb) { |
874 | 0 | exec_state_ = LDBCommandExecuteResult::Failed("DB is not DBImpl"); |
875 | 0 | return; |
876 | 0 | } |
877 | 0 | string rtype1, rtype2, row, val; |
878 | 0 | rtype2 = ""; |
879 | 0 | uint64_t c = 0; |
880 | 0 | uint64_t s1 = 0, s2 = 0; |
881 | | // Setup internal key iterator |
882 | 0 | Arena arena; |
883 | 0 | ScopedArenaIterator iter(idb->NewInternalIterator(&arena)); |
884 | 0 | Status st = iter->status(); |
885 | 0 | if (!st.ok()) { |
886 | 0 | exec_state_ = |
887 | 0 | LDBCommandExecuteResult::Failed("Iterator error:" + st.ToString()); |
888 | 0 | } |
889 | |
|
890 | 0 | if (has_from_) { |
891 | 0 | InternalKey ikey = InternalKey::MaxPossibleForUserKey(from_); |
892 | 0 | iter->Seek(ikey.Encode()); |
893 | 0 | } else { |
894 | 0 | iter->SeekToFirst(); |
895 | 0 | } |
896 | |
|
897 | 0 | int64_t count = 0; |
898 | 0 | for (; iter->Valid(); iter->Next()) { |
899 | 0 | ParsedInternalKey ikey; |
900 | 0 | if (!ParseInternalKey(iter->key(), &ikey)) { |
901 | 0 | fprintf(stderr, "Internal Key [%s] parse error!\n", |
902 | 0 | iter->key().ToString(true /* in hex*/).data()); |
903 | | // TODO: add error counter |
904 | 0 | continue; |
905 | 0 | } |
906 | | |
907 | | // If end marker was specified, we stop before it |
908 | 0 | if (has_to_ && options_.comparator->Compare(ikey.user_key, to_) >= 0) { |
909 | 0 | break; |
910 | 0 | } |
911 | | |
912 | 0 | ++count; |
913 | 0 | int k; |
914 | 0 | if (count_delim_) { |
915 | 0 | rtype1 = ""; |
916 | 0 | s1 = 0; |
917 | 0 | row = iter->key().ToString(); |
918 | 0 | val = iter->value().ToString(); |
919 | 0 | for (k = 0; row[k] != '\x01' && row[k] != '\0'; k++) |
920 | 0 | s1++; |
921 | 0 | for (k = 0; val[k] != '\x01' && val[k] != '\0'; k++) |
922 | 0 | s1++; |
923 | 0 | for (int j = 0; row[j] != delim_[0] && row[j] != '\0' && row[j] != '\x01'; j++) |
924 | 0 | rtype1 += row[j]; |
925 | 0 | if (rtype2.compare("") && rtype2.compare(rtype1) != 0) { |
926 | 0 | fprintf(stdout, "%s => count:%" PRIu64 "\tsize:%" PRIu64 "\n", rtype2.c_str(), c, s2); |
927 | 0 | c = 1; |
928 | 0 | s2 = s1; |
929 | 0 | rtype2 = rtype1; |
930 | 0 | } else { |
931 | 0 | c++; |
932 | 0 | s2 += s1; |
933 | 0 | rtype2 = rtype1; |
934 | 0 | } |
935 | 0 | } |
936 | |
|
937 | 0 | if (!count_only_ && !count_delim_) { |
938 | 0 | string key = ikey.DebugString(is_key_hex_); |
939 | 0 | string value = iter->value().ToString(is_value_hex_); |
940 | 0 | std::cout << key << " => " << value << "\n"; |
941 | 0 | } |
942 | | |
943 | | // Terminate if maximum number of keys have been dumped |
944 | 0 | if (max_keys_ > 0 && count >= max_keys_) break; |
945 | 0 | } |
946 | 0 | if(count_delim_) { |
947 | 0 | fprintf(stdout, "%s => count:%" PRIu64 "\tsize:%" PRIu64 "\n", rtype2.c_str(), c, s2); |
948 | 0 | } else { |
949 | 0 | fprintf(stdout, "Internal keys in range: %" PRId64 "\n", count); |
950 | 0 | } |
951 | 0 | } |
952 | | |
953 | | |
954 | | const string DBDumperCommand::ARG_COUNT_ONLY = "count_only"; |
955 | | const string DBDumperCommand::ARG_COUNT_DELIM = "count_delim"; |
956 | | const string DBDumperCommand::ARG_STATS = "stats"; |
957 | | const string DBDumperCommand::ARG_TTL_BUCKET = "bucket"; |
958 | | |
959 | | DBDumperCommand::DBDumperCommand(const vector<string>& params, |
960 | | const map<string, string>& options, |
961 | | const vector<string>& flags) |
962 | | : LDBCommand(options, flags, true, |
963 | | BuildCmdLineOptions( |
964 | | {ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, ARG_FROM, |
965 | | ARG_TO, ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_COUNT_DELIM, |
966 | | ARG_STATS, ARG_TTL_START, ARG_TTL_END, ARG_TTL_BUCKET, |
967 | | ARG_TIMESTAMP, ARG_PATH})), |
968 | | null_from_(true), |
969 | | null_to_(true), |
970 | | max_keys_(-1), |
971 | | count_only_(false), |
972 | | count_delim_(false), |
973 | 0 | print_stats_(false) { |
974 | 0 | map<string, string>::const_iterator itr = options.find(ARG_FROM); |
975 | 0 | if (itr != options.end()) { |
976 | 0 | null_from_ = false; |
977 | 0 | from_ = itr->second; |
978 | 0 | } |
979 | |
|
980 | 0 | itr = options.find(ARG_TO); |
981 | 0 | if (itr != options.end()) { |
982 | 0 | null_to_ = false; |
983 | 0 | to_ = itr->second; |
984 | 0 | } |
985 | |
|
986 | 0 | itr = options.find(ARG_MAX_KEYS); |
987 | 0 | if (itr != options.end()) { |
988 | 0 | try { |
989 | | #if defined(CYGWIN) |
990 | | max_keys_ = strtol(itr->second.c_str(), 0, 10); |
991 | | #else |
992 | 0 | max_keys_ = stoi(itr->second); |
993 | 0 | #endif |
994 | 0 | } catch(const std::invalid_argument&) { |
995 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(ARG_MAX_KEYS + |
996 | 0 | " has an invalid value"); |
997 | 0 | } catch(const std::out_of_range&) { |
998 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
999 | 0 | ARG_MAX_KEYS + " has a value out-of-range"); |
1000 | 0 | } |
1001 | 0 | } |
1002 | 0 | itr = options.find(ARG_COUNT_DELIM); |
1003 | 0 | if (itr != options.end()) { |
1004 | 0 | delim_ = itr->second; |
1005 | 0 | count_delim_ = true; |
1006 | 0 | } else { |
1007 | 0 | count_delim_ = IsFlagPresent(flags, ARG_COUNT_DELIM); |
1008 | 0 | delim_ = "."; |
1009 | 0 | } |
1010 | |
|
1011 | 0 | print_stats_ = IsFlagPresent(flags, ARG_STATS); |
1012 | 0 | count_only_ = IsFlagPresent(flags, ARG_COUNT_ONLY); |
1013 | |
|
1014 | 0 | if (is_key_hex_) { |
1015 | 0 | if (!null_from_) { |
1016 | 0 | from_ = HexToString(from_); |
1017 | 0 | } |
1018 | 0 | if (!null_to_) { |
1019 | 0 | to_ = HexToString(to_); |
1020 | 0 | } |
1021 | 0 | } |
1022 | |
|
1023 | 0 | itr = options.find(ARG_PATH); |
1024 | 0 | if (itr != options.end()) { |
1025 | 0 | path_ = itr->second; |
1026 | 0 | } |
1027 | 0 | } |
1028 | | |
1029 | 0 | void DBDumperCommand::Help(string& ret) { |
1030 | 0 | ret.append(" "); |
1031 | 0 | ret.append(DBDumperCommand::Name()); |
1032 | 0 | ret.append(HelpRangeCmdArgs()); |
1033 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
1034 | 0 | ret.append(" [--" + ARG_MAX_KEYS + "=<N>]"); |
1035 | 0 | ret.append(" [--" + ARG_TIMESTAMP + "]"); |
1036 | 0 | ret.append(" [--" + ARG_COUNT_ONLY + "]"); |
1037 | 0 | ret.append(" [--" + ARG_COUNT_DELIM + "=<char>]"); |
1038 | 0 | ret.append(" [--" + ARG_STATS + "]"); |
1039 | 0 | ret.append(" [--" + ARG_TTL_BUCKET + "=<N>]"); |
1040 | 0 | ret.append(" [--" + ARG_TTL_START + "=<N>:- is inclusive]"); |
1041 | 0 | ret.append(" [--" + ARG_TTL_END + "=<N>:- is exclusive]"); |
1042 | 0 | ret.append(" [--" + ARG_PATH + "=<path_to_a_file>]"); |
1043 | 0 | ret.append("\n"); |
1044 | 0 | } |
1045 | | |
1046 | | /** |
1047 | | * Handles two separate cases: |
1048 | | * |
1049 | | * 1) --db is specified - just dump the database. |
1050 | | * |
1051 | | * 2) --path is specified - determine based on file extension what dumping |
1052 | | * function to call. Please note that we intentionally use the extension |
1053 | | * and avoid probing the file contents under the assumption that renaming |
1054 | | * the files is not a supported scenario. |
1055 | | * |
1056 | | */ |
1057 | 0 | void DBDumperCommand::DoCommand() { |
1058 | 0 | if (!db_) { |
1059 | 0 | assert(!path_.empty()); |
1060 | 0 | string fileName = GetFileNameFromPath(path_); |
1061 | 0 | uint64_t number; |
1062 | 0 | FileType type; |
1063 | |
|
1064 | 0 | exec_state_ = LDBCommandExecuteResult::Succeed(""); |
1065 | |
|
1066 | 0 | if (!ParseFileName(fileName, &number, &type)) { |
1067 | 0 | exec_state_ = |
1068 | 0 | LDBCommandExecuteResult::Failed("Can't parse file type: " + path_); |
1069 | 0 | return; |
1070 | 0 | } |
1071 | | |
1072 | 0 | switch (type) { |
1073 | 0 | case kLogFile: |
1074 | 0 | DumpWalFile(path_, /* print_header_ */ true, /* print_values_ */ true, |
1075 | 0 | &exec_state_); |
1076 | 0 | break; |
1077 | 0 | case kTableFile: |
1078 | 0 | DumpSstFile(path_, is_key_hex_, /* show_properties */ true); |
1079 | 0 | break; |
1080 | 0 | case kDescriptorFile: |
1081 | 0 | DumpManifestFile(path_, /* verbose_ */ false, is_key_hex_); |
1082 | 0 | break; |
1083 | 0 | default: |
1084 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1085 | 0 | "File type not supported: " + path_); |
1086 | 0 | break; |
1087 | 0 | } |
1088 | | |
1089 | 0 | } else { |
1090 | 0 | DoDumpCommand(); |
1091 | 0 | } |
1092 | 0 | } |
1093 | | |
1094 | 0 | void DBDumperCommand::DoDumpCommand() { |
1095 | 0 | assert(nullptr != db_); |
1096 | 0 | assert(path_.empty()); |
1097 | | |
1098 | | // Parse command line args |
1099 | 0 | uint64_t count = 0; |
1100 | 0 | if (print_stats_) { |
1101 | 0 | string stats; |
1102 | 0 | if (db_->GetProperty("rocksdb.stats", &stats)) { |
1103 | 0 | fprintf(stdout, "%s\n", stats.c_str()); |
1104 | 0 | } |
1105 | 0 | } |
1106 | | |
1107 | | // Setup key iterator |
1108 | 0 | Iterator* iter = db_->NewIterator(ReadOptions(), GetCfHandle()); |
1109 | 0 | Status st = iter->status(); |
1110 | 0 | if (!st.ok()) { |
1111 | 0 | exec_state_ = |
1112 | 0 | LDBCommandExecuteResult::Failed("Iterator error." + st.ToString()); |
1113 | 0 | } |
1114 | |
|
1115 | 0 | if (!null_from_) { |
1116 | 0 | iter->Seek(from_); |
1117 | 0 | } else { |
1118 | 0 | iter->SeekToFirst(); |
1119 | 0 | } |
1120 | |
|
1121 | 0 | int max_keys = max_keys_; |
1122 | 0 | int ttl_start; |
1123 | 0 | if (!ParseIntOption(option_map_, ARG_TTL_START, ttl_start, exec_state_)) { |
1124 | 0 | ttl_start = DBWithTTLImpl::kMinTimestamp; // TTL introduction time |
1125 | 0 | } |
1126 | 0 | int ttl_end; |
1127 | 0 | if (!ParseIntOption(option_map_, ARG_TTL_END, ttl_end, exec_state_)) { |
1128 | 0 | ttl_end = DBWithTTLImpl::kMaxTimestamp; // Max time allowed by TTL feature |
1129 | 0 | } |
1130 | 0 | if (ttl_end < ttl_start) { |
1131 | 0 | fprintf(stderr, "Error: End time can't be less than start time\n"); |
1132 | 0 | delete iter; |
1133 | 0 | return; |
1134 | 0 | } |
1135 | 0 | int time_range = ttl_end - ttl_start; |
1136 | 0 | int bucket_size; |
1137 | 0 | if (!ParseIntOption(option_map_, ARG_TTL_BUCKET, bucket_size, exec_state_) || |
1138 | 0 | bucket_size <= 0) { |
1139 | 0 | bucket_size = time_range; // Will have just 1 bucket by default |
1140 | 0 | } |
1141 | | // Creating variables for row count of each type |
1142 | 0 | string rtype1, rtype2, row, val; |
1143 | 0 | rtype2 = ""; |
1144 | 0 | uint64_t c = 0; |
1145 | 0 | uint64_t s1 = 0, s2 = 0; |
1146 | | |
1147 | | // At this point, bucket_size=0 => time_range=0 |
1148 | 0 | int num_buckets = (bucket_size >= time_range) |
1149 | 0 | ? 1 |
1150 | 0 | : ((time_range + bucket_size - 1) / bucket_size); |
1151 | 0 | vector<uint64_t> bucket_counts(num_buckets, 0); |
1152 | 0 | if (is_db_ttl_ && !count_only_ && timestamp_ && !count_delim_) { |
1153 | 0 | fprintf(stdout, "Dumping key-values from %s to %s\n", |
1154 | 0 | ReadableTime(ttl_start).c_str(), ReadableTime(ttl_end).c_str()); |
1155 | 0 | } |
1156 | |
|
1157 | 0 | for (; iter->Valid(); iter->Next()) { |
1158 | 0 | int rawtime = 0; |
1159 | | // If end marker was specified, we stop before it |
1160 | 0 | if (!null_to_ && (iter->key().ToString() >= to_)) |
1161 | 0 | break; |
1162 | | // Terminate if maximum number of keys have been dumped |
1163 | 0 | if (max_keys == 0) |
1164 | 0 | break; |
1165 | 0 | if (is_db_ttl_) { |
1166 | 0 | TtlIterator* it_ttl = dynamic_cast<TtlIterator*>(iter); |
1167 | 0 | assert(it_ttl); |
1168 | 0 | rawtime = it_ttl->timestamp(); |
1169 | 0 | if (rawtime < ttl_start || rawtime >= ttl_end) { |
1170 | 0 | continue; |
1171 | 0 | } |
1172 | 0 | } |
1173 | 0 | if (max_keys > 0) { |
1174 | 0 | --max_keys; |
1175 | 0 | } |
1176 | 0 | if (is_db_ttl_ && num_buckets > 1) { |
1177 | 0 | IncBucketCounts(&bucket_counts, ttl_start, time_range, bucket_size, |
1178 | 0 | rawtime, num_buckets); |
1179 | 0 | } |
1180 | 0 | ++count; |
1181 | 0 | if (count_delim_) { |
1182 | 0 | rtype1 = ""; |
1183 | 0 | row = iter->key().ToString(); |
1184 | 0 | val = iter->value().ToString(); |
1185 | 0 | s1 = row.size()+val.size(); |
1186 | 0 | for (int j = 0; row[j] != delim_[0] && row[j] != '\0'; j++) |
1187 | 0 | rtype1 += row[j]; |
1188 | 0 | if (rtype2.compare("") && rtype2.compare(rtype1) != 0) { |
1189 | 0 | fprintf(stdout, "%s => count:%" PRIu64 "\tsize:%" PRIu64 "\n", rtype2.c_str(), c, s2); |
1190 | 0 | c = 1; |
1191 | 0 | s2 = s1; |
1192 | 0 | rtype2 = rtype1; |
1193 | 0 | } else { |
1194 | 0 | c++; |
1195 | 0 | s2 += s1; |
1196 | 0 | rtype2 = rtype1; |
1197 | 0 | } |
1198 | |
|
1199 | 0 | } |
1200 | | |
1201 | | |
1202 | |
|
1203 | 0 | if (!count_only_ && !count_delim_) { |
1204 | 0 | if (is_db_ttl_ && timestamp_) { |
1205 | 0 | fprintf(stdout, "%s ", ReadableTime(rawtime).c_str()); |
1206 | 0 | } |
1207 | 0 | string str = PrintKeyValue(iter->key().ToString(), |
1208 | 0 | iter->value().ToString(), is_key_hex_, |
1209 | 0 | is_value_hex_); |
1210 | 0 | fprintf(stdout, "%s\n", str.c_str()); |
1211 | 0 | } |
1212 | 0 | } |
1213 | |
|
1214 | 0 | if (num_buckets > 1 && is_db_ttl_) { |
1215 | 0 | PrintBucketCounts(bucket_counts, ttl_start, ttl_end, bucket_size, |
1216 | 0 | num_buckets); |
1217 | 0 | } else if(count_delim_) { |
1218 | 0 | fprintf(stdout, "%s => count:%" PRIu64 "\tsize:%" PRIu64 "\n", rtype2.c_str(), c, s2); |
1219 | 0 | } else { |
1220 | 0 | fprintf(stdout, "Keys in range: %" PRIu64 "\n", count); |
1221 | 0 | } |
1222 | | // Clean up |
1223 | 0 | delete iter; |
1224 | 0 | } |
1225 | | |
1226 | | const string ReduceDBLevelsCommand::ARG_NEW_LEVELS = "new_levels"; |
1227 | | const string ReduceDBLevelsCommand::ARG_PRINT_OLD_LEVELS = "print_old_levels"; |
1228 | | |
1229 | | ReduceDBLevelsCommand::ReduceDBLevelsCommand(const vector<string>& params, |
1230 | | const map<string, string>& options, const vector<string>& flags) : |
1231 | | LDBCommand(options, flags, false, |
1232 | | BuildCmdLineOptions({ARG_NEW_LEVELS, ARG_PRINT_OLD_LEVELS})), |
1233 | | old_levels_(1 << 7), |
1234 | | new_levels_(-1), |
1235 | 8 | print_old_levels_(false) { |
1236 | | |
1237 | | |
1238 | 8 | ParseIntOption(option_map_, ARG_NEW_LEVELS, new_levels_, exec_state_); |
1239 | 8 | print_old_levels_ = IsFlagPresent(flags, ARG_PRINT_OLD_LEVELS); |
1240 | | |
1241 | 8 | if(new_levels_ <= 0) { |
1242 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1243 | 0 | " Use --" + ARG_NEW_LEVELS + " to specify a new level number\n"); |
1244 | 0 | } |
1245 | 8 | } |
1246 | | |
1247 | | vector<string> ReduceDBLevelsCommand::PrepareArgs(const string& db_path, |
1248 | 8 | int new_levels, bool print_old_level) { |
1249 | 8 | vector<string> ret; |
1250 | 8 | ret.push_back("reduce_levels"); |
1251 | 8 | ret.push_back("--" + ARG_DB + "=" + db_path); |
1252 | 8 | ret.push_back("--" + ARG_NEW_LEVELS + "=" + rocksdb::ToString(new_levels)); |
1253 | 8 | if(print_old_level) { |
1254 | 0 | ret.push_back("--" + ARG_PRINT_OLD_LEVELS); |
1255 | 0 | } |
1256 | 8 | return ret; |
1257 | 8 | } |
1258 | | |
1259 | 0 | void ReduceDBLevelsCommand::Help(string& ret) { |
1260 | 0 | ret.append(" "); |
1261 | 0 | ret.append(ReduceDBLevelsCommand::Name()); |
1262 | 0 | ret.append(" --" + ARG_NEW_LEVELS + "=<New number of levels>"); |
1263 | 0 | ret.append(" [--" + ARG_PRINT_OLD_LEVELS + "]"); |
1264 | 0 | ret.append("\n"); |
1265 | 0 | } |
1266 | | |
1267 | 13 | Options ReduceDBLevelsCommand::PrepareOptionsForOpenDB() { |
1268 | 13 | Options opt = LDBCommand::PrepareOptionsForOpenDB(); |
1269 | 13 | opt.num_levels = old_levels_; |
1270 | 13 | opt.max_bytes_for_level_multiplier_additional.resize(opt.num_levels, 1); |
1271 | | // Disable size compaction |
1272 | 13 | opt.max_bytes_for_level_base = 1ULL << 50; |
1273 | 13 | opt.max_bytes_for_level_multiplier = 1; |
1274 | 13 | return opt; |
1275 | 13 | } |
1276 | | |
1277 | | Status ReduceDBLevelsCommand::GetOldNumOfLevels(Options& opt, |
1278 | 8 | int* levels) { |
1279 | 8 | EnvOptions soptions; |
1280 | 8 | std::shared_ptr<Cache> tc( |
1281 | 8 | NewLRUCache(opt.max_open_files - 10, opt.table_cache_numshardbits)); |
1282 | 8 | const InternalKeyComparator cmp(opt.comparator); |
1283 | 8 | WriteController wc(opt.delayed_write_rate); |
1284 | 8 | WriteBuffer wb(opt.db_write_buffer_size); |
1285 | 8 | VersionSet versions(db_path_, &opt, soptions, tc.get(), &wb, &wc); |
1286 | 8 | std::vector<ColumnFamilyDescriptor> dummy; |
1287 | 8 | ColumnFamilyDescriptor dummy_descriptor(kDefaultColumnFamilyName, |
1288 | 8 | ColumnFamilyOptions(opt)); |
1289 | 8 | dummy.push_back(dummy_descriptor); |
1290 | | // We rely the VersionSet::Recover to tell us the internal data structures |
1291 | | // in the db. And the Recover() should never do any change |
1292 | | // (like LogAndApply) to the manifest file. |
1293 | 8 | Status st = versions.Recover(dummy); |
1294 | 8 | if (!st.ok()) { |
1295 | 0 | return st; |
1296 | 0 | } |
1297 | 8 | int max = -1; |
1298 | 8 | auto default_cfd = versions.GetColumnFamilySet()->GetDefault(); |
1299 | 1.03k | for (int i = 0; i < default_cfd->NumberLevels(); i++) { |
1300 | 1.02k | if (default_cfd->current()->storage_info()->NumLevelFiles(i)) { |
1301 | 11 | max = i; |
1302 | 11 | } |
1303 | 1.02k | } |
1304 | | |
1305 | 8 | *levels = max + 1; |
1306 | 8 | return st; |
1307 | 8 | } |
1308 | | |
1309 | 8 | void ReduceDBLevelsCommand::DoCommand() { |
1310 | 8 | if (new_levels_ <= 1) { |
1311 | 0 | exec_state_ = |
1312 | 0 | LDBCommandExecuteResult::Failed("Invalid number of levels.\n"); |
1313 | 0 | return; |
1314 | 0 | } |
1315 | | |
1316 | 8 | Status st; |
1317 | 8 | Options opt = PrepareOptionsForOpenDB(); |
1318 | 8 | int old_level_num = -1; |
1319 | 8 | st = GetOldNumOfLevels(opt, &old_level_num); |
1320 | 8 | if (!st.ok()) { |
1321 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
1322 | 0 | return; |
1323 | 0 | } |
1324 | | |
1325 | 8 | if (print_old_levels_) { |
1326 | 0 | fprintf(stdout, "The old number of levels in use is %d\n", old_level_num); |
1327 | 0 | } |
1328 | | |
1329 | 8 | if (old_level_num <= new_levels_) { |
1330 | 3 | return; |
1331 | 3 | } |
1332 | | |
1333 | 5 | old_levels_ = old_level_num; |
1334 | | |
1335 | 5 | OpenDB(); |
1336 | 5 | if (!db_) { |
1337 | 0 | return; |
1338 | 0 | } |
1339 | | // Compact the whole DB to put all files to the highest level. |
1340 | 5 | fprintf(stdout, "Compacting the db...\n"); |
1341 | 5 | CHECK_OK(db_->CompactRange(CompactRangeOptions(), GetCfHandle(), nullptr, nullptr)); |
1342 | 5 | CloseDB(); |
1343 | | |
1344 | 5 | EnvOptions soptions; |
1345 | 5 | st = VersionSet::ReduceNumberOfLevels(db_path_, &opt, soptions, new_levels_); |
1346 | 5 | if (!st.ok()) { |
1347 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
1348 | 0 | return; |
1349 | 0 | } |
1350 | 5 | } |
1351 | | |
1352 | | const string ChangeCompactionStyleCommand::ARG_OLD_COMPACTION_STYLE = |
1353 | | "old_compaction_style"; |
1354 | | const string ChangeCompactionStyleCommand::ARG_NEW_COMPACTION_STYLE = |
1355 | | "new_compaction_style"; |
1356 | | |
1357 | | ChangeCompactionStyleCommand::ChangeCompactionStyleCommand( |
1358 | | const vector<string>& params, const map<string, string>& options, |
1359 | | const vector<string>& flags) : |
1360 | | LDBCommand(options, flags, false, |
1361 | | BuildCmdLineOptions({ARG_OLD_COMPACTION_STYLE, |
1362 | | ARG_NEW_COMPACTION_STYLE})), |
1363 | | old_compaction_style_(-1), |
1364 | 0 | new_compaction_style_(-1) { |
1365 | |
|
1366 | 0 | ParseIntOption(option_map_, ARG_OLD_COMPACTION_STYLE, old_compaction_style_, |
1367 | 0 | exec_state_); |
1368 | 0 | if (old_compaction_style_ != kCompactionStyleLevel && |
1369 | 0 | old_compaction_style_ != kCompactionStyleUniversal) { |
1370 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1371 | 0 | "Use --" + ARG_OLD_COMPACTION_STYLE + " to specify old compaction " + |
1372 | 0 | "style. Check ldb help for proper compaction style value.\n"); |
1373 | 0 | return; |
1374 | 0 | } |
1375 | | |
1376 | 0 | ParseIntOption(option_map_, ARG_NEW_COMPACTION_STYLE, new_compaction_style_, |
1377 | 0 | exec_state_); |
1378 | 0 | if (new_compaction_style_ != kCompactionStyleLevel && |
1379 | 0 | new_compaction_style_ != kCompactionStyleUniversal) { |
1380 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1381 | 0 | "Use --" + ARG_NEW_COMPACTION_STYLE + " to specify new compaction " + |
1382 | 0 | "style. Check ldb help for proper compaction style value.\n"); |
1383 | 0 | return; |
1384 | 0 | } |
1385 | | |
1386 | 0 | if (new_compaction_style_ == old_compaction_style_) { |
1387 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1388 | 0 | "Old compaction style is the same as new compaction style. " |
1389 | 0 | "Nothing to do.\n"); |
1390 | 0 | return; |
1391 | 0 | } |
1392 | | |
1393 | 0 | if (old_compaction_style_ == kCompactionStyleUniversal && |
1394 | 0 | new_compaction_style_ == kCompactionStyleLevel) { |
1395 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1396 | 0 | "Convert from universal compaction to level compaction. " |
1397 | 0 | "Nothing to do.\n"); |
1398 | 0 | return; |
1399 | 0 | } |
1400 | 0 | } |
1401 | | |
1402 | 0 | void ChangeCompactionStyleCommand::Help(string& ret) { |
1403 | 0 | ret.append(" "); |
1404 | 0 | ret.append(ChangeCompactionStyleCommand::Name()); |
1405 | 0 | ret.append(" --" + ARG_OLD_COMPACTION_STYLE + "=<Old compaction style: 0 " + |
1406 | 0 | "for level compaction, 1 for universal compaction>"); |
1407 | 0 | ret.append(" --" + ARG_NEW_COMPACTION_STYLE + "=<New compaction style: 0 " + |
1408 | 0 | "for level compaction, 1 for universal compaction>"); |
1409 | 0 | ret.append("\n"); |
1410 | 0 | } |
1411 | | |
1412 | 0 | Options ChangeCompactionStyleCommand::PrepareOptionsForOpenDB() { |
1413 | 0 | Options opt = LDBCommand::PrepareOptionsForOpenDB(); |
1414 | |
|
1415 | 0 | if (old_compaction_style_ == kCompactionStyleLevel && |
1416 | 0 | new_compaction_style_ == kCompactionStyleUniversal) { |
1417 | | // In order to convert from level compaction to universal compaction, we |
1418 | | // need to compact all data into a single file and move it to level 0. |
1419 | 0 | opt.disable_auto_compactions = true; |
1420 | 0 | opt.target_file_size_base = INT_MAX; |
1421 | 0 | opt.target_file_size_multiplier = 1; |
1422 | 0 | opt.max_bytes_for_level_base = INT_MAX; |
1423 | 0 | opt.max_bytes_for_level_multiplier = 1; |
1424 | 0 | } |
1425 | |
|
1426 | 0 | return opt; |
1427 | 0 | } |
1428 | | |
1429 | 0 | void ChangeCompactionStyleCommand::DoCommand() { |
1430 | | // print db stats before we have made any change |
1431 | 0 | std::string property; |
1432 | 0 | std::string files_per_level; |
1433 | 0 | for (int i = 0; i < db_->NumberLevels(GetCfHandle()); i++) { |
1434 | 0 | db_->GetProperty(GetCfHandle(), |
1435 | 0 | "rocksdb.num-files-at-level" + NumberToString(i), |
1436 | 0 | &property); |
1437 | | |
1438 | | // format print string |
1439 | 0 | char buf[100]; |
1440 | 0 | snprintf(buf, sizeof(buf), "%s%s", (i ? "," : ""), property.c_str()); |
1441 | 0 | files_per_level += buf; |
1442 | 0 | } |
1443 | 0 | fprintf(stdout, "files per level before compaction: %s\n", |
1444 | 0 | files_per_level.c_str()); |
1445 | | |
1446 | | // manual compact into a single file and move the file to level 0 |
1447 | 0 | CompactRangeOptions compact_options; |
1448 | 0 | compact_options.change_level = true; |
1449 | 0 | compact_options.target_level = 0; |
1450 | 0 | CHECK_OK(db_->CompactRange(compact_options, GetCfHandle(), nullptr, nullptr)); |
1451 | | |
1452 | | // verify compaction result |
1453 | 0 | files_per_level = ""; |
1454 | 0 | int num_files = 0; |
1455 | 0 | for (int i = 0; i < db_->NumberLevels(); i++) { |
1456 | 0 | db_->GetProperty(GetCfHandle(), |
1457 | 0 | "rocksdb.num-files-at-level" + NumberToString(i), |
1458 | 0 | &property); |
1459 | | |
1460 | | // format print string |
1461 | 0 | char buf[100]; |
1462 | 0 | snprintf(buf, sizeof(buf), "%s%s", (i ? "," : ""), property.c_str()); |
1463 | 0 | files_per_level += buf; |
1464 | |
|
1465 | 0 | num_files = atoi(property.c_str()); |
1466 | | |
1467 | | // level 0 should have only 1 file |
1468 | 0 | if (i == 0 && num_files != 1) { |
1469 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1470 | 0 | "Number of db files at " |
1471 | 0 | "level 0 after compaction is " + |
1472 | 0 | ToString(num_files) + ", not 1.\n"); |
1473 | 0 | return; |
1474 | 0 | } |
1475 | | // other levels should have no file |
1476 | 0 | if (i > 0 && num_files != 0) { |
1477 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1478 | 0 | "Number of db files at " |
1479 | 0 | "level " + |
1480 | 0 | ToString(i) + " after compaction is " + ToString(num_files) + |
1481 | 0 | ", not 0.\n"); |
1482 | 0 | return; |
1483 | 0 | } |
1484 | 0 | } |
1485 | |
|
1486 | 0 | fprintf(stdout, "files per level after compaction: %s\n", |
1487 | 0 | files_per_level.c_str()); |
1488 | 0 | } |
1489 | | |
1490 | | // ---------------------------------------------------------------------------- |
1491 | | |
1492 | | namespace { |
1493 | | |
1494 | | struct StdErrReporter : public log::Reader::Reporter { |
1495 | 0 | void Corruption(size_t bytes, const Status& s) override { |
1496 | 0 | std::cerr << "Corruption detected in log file " << s.ToString() << "\n"; |
1497 | 0 | } |
1498 | | }; |
1499 | | |
1500 | | class InMemoryHandler : public WriteBatch::Handler { |
1501 | | public: |
1502 | 0 | InMemoryHandler(std::stringstream& row, bool print_values) : Handler(), row_(row) { |
1503 | 0 | print_values_ = print_values; |
1504 | 0 | } |
1505 | | |
1506 | 0 | void commonPutMerge(const Slice& key, const Slice& value) { |
1507 | 0 | string k = LDBCommand::StringToHex(key.ToString()); |
1508 | 0 | if (print_values_) { |
1509 | 0 | string v = LDBCommand::StringToHex(value.ToString()); |
1510 | 0 | row_ << k << " : "; |
1511 | 0 | row_ << v << " "; |
1512 | 0 | } else { |
1513 | 0 | row_ << k << " "; |
1514 | 0 | } |
1515 | 0 | } |
1516 | | |
1517 | 0 | void Put(const Slice& key, const Slice& value) override { |
1518 | 0 | row_ << "PUT : "; |
1519 | 0 | commonPutMerge(key, value); |
1520 | 0 | } |
1521 | | |
1522 | 0 | void Merge(const Slice& key, const Slice& value) override { |
1523 | 0 | row_ << "MERGE : "; |
1524 | 0 | commonPutMerge(key, value); |
1525 | 0 | } |
1526 | | |
1527 | 0 | void Delete(const Slice& key) override { |
1528 | 0 | row_ << "DELETE : "; |
1529 | 0 | row_ << LDBCommand::StringToHex(key.ToBuffer()) << " "; |
1530 | 0 | } |
1531 | | |
1532 | 0 | Status Frontiers(const UserFrontiers& range) override { |
1533 | 0 | row_ << " MARGIN_RANGE : " << range.ToString() << " "; |
1534 | 0 | return Status::OK(); |
1535 | 0 | } |
1536 | | |
1537 | 0 | virtual ~InMemoryHandler() {} |
1538 | | |
1539 | | private: |
1540 | | std::stringstream & row_; |
1541 | | bool print_values_; |
1542 | | }; |
1543 | | |
1544 | | void DumpWalFile(std::string wal_file, bool print_header, bool print_values, |
1545 | 0 | LDBCommandExecuteResult* exec_state) { |
1546 | 0 | Env* env_ = Env::Default(); |
1547 | 0 | EnvOptions soptions; |
1548 | 0 | unique_ptr<SequentialFileReader> wal_file_reader; |
1549 | |
|
1550 | 0 | Status status; |
1551 | 0 | { |
1552 | 0 | unique_ptr<SequentialFile> file; |
1553 | 0 | status = env_->NewSequentialFile(wal_file, &file, soptions); |
1554 | 0 | if (status.ok()) { |
1555 | 0 | wal_file_reader.reset(new SequentialFileReader(std::move(file))); |
1556 | 0 | } |
1557 | 0 | } |
1558 | 0 | if (!status.ok()) { |
1559 | 0 | if (exec_state) { |
1560 | 0 | *exec_state = LDBCommandExecuteResult::Failed("Failed to open WAL file " + |
1561 | 0 | status.ToString()); |
1562 | 0 | } else { |
1563 | 0 | std::cerr << "Error: Failed to open WAL file " << status.ToString() |
1564 | 0 | << std::endl; |
1565 | 0 | } |
1566 | 0 | } else { |
1567 | 0 | StdErrReporter reporter; |
1568 | 0 | uint64_t log_number; |
1569 | 0 | FileType type; |
1570 | | |
1571 | | // we need the log number, but ParseFilename expects dbname/NNN.log. |
1572 | 0 | string sanitized = wal_file; |
1573 | 0 | size_t lastslash = sanitized.rfind('/'); |
1574 | 0 | if (lastslash != std::string::npos) |
1575 | 0 | sanitized = sanitized.substr(lastslash + 1); |
1576 | 0 | if (!ParseFileName(sanitized, &log_number, &type)) { |
1577 | | // bogus input, carry on as best we can |
1578 | 0 | log_number = 0; |
1579 | 0 | } |
1580 | 0 | DBOptions db_options; |
1581 | 0 | log::Reader reader(db_options.info_log, move(wal_file_reader), &reporter, |
1582 | 0 | true, 0, log_number); |
1583 | 0 | string scratch; |
1584 | 0 | WriteBatch batch; |
1585 | 0 | Slice record; |
1586 | 0 | std::stringstream row; |
1587 | 0 | if (print_header) { |
1588 | 0 | std::cout << "Sequence,Count,ByteSize,Physical Offset,Key(s)"; |
1589 | 0 | if (print_values) { |
1590 | 0 | std::cout << " : value "; |
1591 | 0 | } |
1592 | 0 | std::cout << "\n"; |
1593 | 0 | } |
1594 | 0 | while (reader.ReadRecord(&record, &scratch)) { |
1595 | 0 | row.str(""); |
1596 | 0 | if (record.size() < 12) { |
1597 | 0 | reporter.Corruption(record.size(), |
1598 | 0 | STATUS(Corruption, "log record too small")); |
1599 | 0 | } else { |
1600 | 0 | WriteBatchInternal::SetContents(&batch, record); |
1601 | 0 | row << WriteBatchInternal::Sequence(&batch) << ","; |
1602 | 0 | row << WriteBatchInternal::Count(&batch) << ","; |
1603 | 0 | row << WriteBatchInternal::ByteSize(&batch) << ","; |
1604 | 0 | row << reader.LastRecordOffset() << ","; |
1605 | 0 | InMemoryHandler handler(row, print_values); |
1606 | 0 | CHECK_OK(batch.Iterate(&handler)); |
1607 | 0 | row << "\n"; |
1608 | 0 | } |
1609 | 0 | std::cout << row.str(); |
1610 | 0 | } |
1611 | 0 | } |
1612 | 0 | } |
1613 | | |
1614 | | } // namespace |
1615 | | |
1616 | | const string WALDumperCommand::ARG_WAL_FILE = "walfile"; |
1617 | | const string WALDumperCommand::ARG_PRINT_VALUE = "print_value"; |
1618 | | const string WALDumperCommand::ARG_PRINT_HEADER = "header"; |
1619 | | |
1620 | | WALDumperCommand::WALDumperCommand(const vector<string>& params, |
1621 | | const map<string, string>& options, const vector<string>& flags) : |
1622 | | LDBCommand(options, flags, true, |
1623 | | BuildCmdLineOptions( |
1624 | | {ARG_WAL_FILE, ARG_PRINT_HEADER, ARG_PRINT_VALUE})), |
1625 | 0 | print_header_(false), print_values_(false) { |
1626 | |
|
1627 | 0 | wal_file_.clear(); |
1628 | |
|
1629 | 0 | map<string, string>::const_iterator itr = options.find(ARG_WAL_FILE); |
1630 | 0 | if (itr != options.end()) { |
1631 | 0 | wal_file_ = itr->second; |
1632 | 0 | } |
1633 | | |
1634 | |
|
1635 | 0 | print_header_ = IsFlagPresent(flags, ARG_PRINT_HEADER); |
1636 | 0 | print_values_ = IsFlagPresent(flags, ARG_PRINT_VALUE); |
1637 | 0 | if (wal_file_.empty()) { |
1638 | 0 | exec_state_ = LDBCommandExecuteResult::Failed("Argument " + ARG_WAL_FILE + |
1639 | 0 | " must be specified."); |
1640 | 0 | } |
1641 | 0 | } |
1642 | | |
1643 | 0 | void WALDumperCommand::Help(string& ret) { |
1644 | 0 | ret.append(" "); |
1645 | 0 | ret.append(WALDumperCommand::Name()); |
1646 | 0 | ret.append(" --" + ARG_WAL_FILE + "=<write_ahead_log_file_path>"); |
1647 | 0 | ret.append(" [--" + ARG_PRINT_HEADER + "] "); |
1648 | 0 | ret.append(" [--" + ARG_PRINT_VALUE + "] "); |
1649 | 0 | ret.append("\n"); |
1650 | 0 | } |
1651 | | |
1652 | 0 | void WALDumperCommand::DoCommand() { |
1653 | 0 | DumpWalFile(wal_file_, print_header_, print_values_, &exec_state_); |
1654 | 0 | } |
1655 | | |
1656 | | // ---------------------------------------------------------------------------- |
1657 | | |
1658 | | GetCommand::GetCommand(const vector<string>& params, |
1659 | | const map<string, string>& options, const vector<string>& flags) : |
1660 | | LDBCommand(options, flags, true, BuildCmdLineOptions({ARG_TTL, ARG_HEX, |
1661 | | ARG_KEY_HEX, |
1662 | 0 | ARG_VALUE_HEX})) { |
1663 | |
|
1664 | 0 | if (params.size() != 1) { |
1665 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1666 | 0 | "<key> must be specified for the get command"); |
1667 | 0 | } else { |
1668 | 0 | key_ = params.at(0); |
1669 | 0 | } |
1670 | |
|
1671 | 0 | if (is_key_hex_) { |
1672 | 0 | key_ = HexToString(key_); |
1673 | 0 | } |
1674 | 0 | } |
1675 | | |
1676 | 0 | void GetCommand::Help(string& ret) { |
1677 | 0 | ret.append(" "); |
1678 | 0 | ret.append(GetCommand::Name()); |
1679 | 0 | ret.append(" <key>"); |
1680 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
1681 | 0 | ret.append("\n"); |
1682 | 0 | } |
1683 | | |
1684 | 0 | void GetCommand::DoCommand() { |
1685 | 0 | if (!db_) { |
1686 | 0 | assert(GetExecuteState().IsFailed()); |
1687 | 0 | return; |
1688 | 0 | } |
1689 | 0 | string value; |
1690 | 0 | Status st = db_->Get(ReadOptions(), GetCfHandle(), key_, &value); |
1691 | 0 | if (st.ok()) { |
1692 | 0 | fprintf(stdout, "%s\n", |
1693 | 0 | (is_value_hex_ ? StringToHex(value) : value).c_str()); |
1694 | 0 | } else { |
1695 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
1696 | 0 | } |
1697 | 0 | } |
1698 | | |
1699 | | // ---------------------------------------------------------------------------- |
1700 | | |
1701 | | ApproxSizeCommand::ApproxSizeCommand(const vector<string>& params, |
1702 | | const map<string, string>& options, const vector<string>& flags) : |
1703 | | LDBCommand(options, flags, true, |
1704 | | BuildCmdLineOptions({ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, |
1705 | 0 | ARG_FROM, ARG_TO})) { |
1706 | |
|
1707 | 0 | if (options.find(ARG_FROM) != options.end()) { |
1708 | 0 | start_key_ = options.find(ARG_FROM)->second; |
1709 | 0 | } else { |
1710 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1711 | 0 | ARG_FROM + " must be specified for approxsize command"); |
1712 | 0 | return; |
1713 | 0 | } |
1714 | | |
1715 | 0 | if (options.find(ARG_TO) != options.end()) { |
1716 | 0 | end_key_ = options.find(ARG_TO)->second; |
1717 | 0 | } else { |
1718 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1719 | 0 | ARG_TO + " must be specified for approxsize command"); |
1720 | 0 | return; |
1721 | 0 | } |
1722 | | |
1723 | 0 | if (is_key_hex_) { |
1724 | 0 | start_key_ = HexToString(start_key_); |
1725 | 0 | end_key_ = HexToString(end_key_); |
1726 | 0 | } |
1727 | 0 | } |
1728 | | |
1729 | 0 | void ApproxSizeCommand::Help(string& ret) { |
1730 | 0 | ret.append(" "); |
1731 | 0 | ret.append(ApproxSizeCommand::Name()); |
1732 | 0 | ret.append(HelpRangeCmdArgs()); |
1733 | 0 | ret.append("\n"); |
1734 | 0 | } |
1735 | | |
1736 | 0 | void ApproxSizeCommand::DoCommand() { |
1737 | 0 | if (!db_) { |
1738 | 0 | assert(GetExecuteState().IsFailed()); |
1739 | 0 | return; |
1740 | 0 | } |
1741 | 0 | Range ranges[1]; |
1742 | 0 | ranges[0] = Range(start_key_, end_key_); |
1743 | 0 | uint64_t sizes[1]; |
1744 | 0 | db_->GetApproximateSizes(GetCfHandle(), ranges, 1, sizes); |
1745 | 0 | fprintf(stdout, "%" PRIu64 "\n", sizes[0]); |
1746 | | /* Weird that GetApproximateSizes() returns void, although documentation |
1747 | | * says that it returns a Status object. |
1748 | | if (!st.ok()) { |
1749 | | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
1750 | | } |
1751 | | */ |
1752 | 0 | } |
1753 | | |
1754 | | // ---------------------------------------------------------------------------- |
1755 | | |
1756 | | BatchPutCommand::BatchPutCommand(const vector<string>& params, |
1757 | | const map<string, string>& options, const vector<string>& flags) : |
1758 | | LDBCommand(options, flags, false, |
1759 | | BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, |
1760 | 0 | ARG_CREATE_IF_MISSING})) { |
1761 | |
|
1762 | 0 | if (params.size() < 2) { |
1763 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1764 | 0 | "At least one <key> <value> pair must be specified batchput."); |
1765 | 0 | } else if (params.size() % 2 != 0) { |
1766 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1767 | 0 | "Equal number of <key>s and <value>s must be specified for batchput."); |
1768 | 0 | } else { |
1769 | 0 | for (size_t i = 0; i < params.size(); i += 2) { |
1770 | 0 | string key = params.at(i); |
1771 | 0 | string value = params.at(i+1); |
1772 | 0 | key_values_.emplace_back(is_key_hex_ ? HexToString(key) : key, |
1773 | 0 | is_value_hex_ ? HexToString(value) : value); |
1774 | 0 | } |
1775 | 0 | } |
1776 | 0 | } |
1777 | | |
1778 | 0 | void BatchPutCommand::Help(string& ret) { |
1779 | 0 | ret.append(" "); |
1780 | 0 | ret.append(BatchPutCommand::Name()); |
1781 | 0 | ret.append(" <key> <value> [<key> <value>] [..]"); |
1782 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
1783 | 0 | ret.append("\n"); |
1784 | 0 | } |
1785 | | |
1786 | 0 | void BatchPutCommand::DoCommand() { |
1787 | 0 | if (!db_) { |
1788 | 0 | assert(GetExecuteState().IsFailed()); |
1789 | 0 | return; |
1790 | 0 | } |
1791 | 0 | WriteBatch batch; |
1792 | |
|
1793 | 0 | for (auto itr = key_values_.begin(); itr != key_values_.end(); ++itr) { |
1794 | 0 | batch.Put(GetCfHandle(), itr->first, itr->second); |
1795 | 0 | } |
1796 | 0 | Status st = db_->Write(WriteOptions(), &batch); |
1797 | 0 | if (st.ok()) { |
1798 | 0 | fprintf(stdout, "OK\n"); |
1799 | 0 | } else { |
1800 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
1801 | 0 | } |
1802 | 0 | } |
1803 | | |
1804 | 0 | Options BatchPutCommand::PrepareOptionsForOpenDB() { |
1805 | 0 | Options opt = LDBCommand::PrepareOptionsForOpenDB(); |
1806 | 0 | opt.create_if_missing = IsFlagPresent(flags_, ARG_CREATE_IF_MISSING); |
1807 | 0 | return opt; |
1808 | 0 | } |
1809 | | |
1810 | | // ---------------------------------------------------------------------------- |
1811 | | |
1812 | | ScanCommand::ScanCommand(const vector<string>& params, |
1813 | | const map<string, string>& options, |
1814 | | const vector<string>& flags) |
1815 | | : LDBCommand(options, flags, true, |
1816 | | BuildCmdLineOptions( |
1817 | | {ARG_TTL, ARG_NO_VALUE, ARG_HEX, ARG_KEY_HEX, |
1818 | | ARG_TO, ARG_VALUE_HEX, ARG_FROM, ARG_TIMESTAMP, |
1819 | | ARG_MAX_KEYS, ARG_TTL_START, ARG_TTL_END, ARG_ONLY_VERIFY_CHECKSUMS})), |
1820 | | start_key_specified_(false), |
1821 | | end_key_specified_(false), |
1822 | | max_keys_scanned_(-1), |
1823 | 2 | no_value_(false) { |
1824 | | |
1825 | 2 | map<string, string>::const_iterator itr = options.find(ARG_FROM); |
1826 | 2 | if (itr != options.end()) { |
1827 | 0 | start_key_ = itr->second; |
1828 | 0 | if (is_key_hex_) { |
1829 | 0 | start_key_ = HexToString(start_key_); |
1830 | 0 | } |
1831 | 0 | start_key_specified_ = true; |
1832 | 0 | } |
1833 | 2 | itr = options.find(ARG_TO); |
1834 | 2 | if (itr != options.end()) { |
1835 | 0 | end_key_ = itr->second; |
1836 | 0 | if (is_key_hex_) { |
1837 | 0 | end_key_ = HexToString(end_key_); |
1838 | 0 | } |
1839 | 0 | end_key_specified_ = true; |
1840 | 0 | } |
1841 | | |
1842 | 2 | vector<string>::const_iterator vitr = |
1843 | 2 | std::find(flags.begin(), flags.end(), ARG_NO_VALUE); |
1844 | 2 | if (vitr != flags.end()) { |
1845 | 0 | no_value_ = true; |
1846 | 0 | } |
1847 | | |
1848 | 2 | vitr = std::find(flags.begin(), flags.end(), ARG_ONLY_VERIFY_CHECKSUMS); |
1849 | 2 | if (vitr != flags.end()) { |
1850 | 2 | LOG(INFO) << "Only verify checksums, don't print entries."; |
1851 | 2 | only_verify_checksums_ = true; |
1852 | 2 | } |
1853 | | |
1854 | 2 | itr = options.find(ARG_MAX_KEYS); |
1855 | 2 | if (itr != options.end()) { |
1856 | 0 | try { |
1857 | | #if defined(CYGWIN) |
1858 | | max_keys_scanned_ = strtol(itr->second.c_str(), 0, 10); |
1859 | | #else |
1860 | 0 | max_keys_scanned_ = stoi(itr->second); |
1861 | 0 | #endif |
1862 | 0 | } catch(const std::invalid_argument&) { |
1863 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(ARG_MAX_KEYS + |
1864 | 0 | " has an invalid value"); |
1865 | 0 | } catch(const std::out_of_range&) { |
1866 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1867 | 0 | ARG_MAX_KEYS + " has a value out-of-range"); |
1868 | 0 | } |
1869 | 0 | } |
1870 | 2 | } |
1871 | | |
1872 | 0 | void ScanCommand::Help(string& ret) { |
1873 | 0 | ret.append(" "); |
1874 | 0 | ret.append(ScanCommand::Name()); |
1875 | 0 | ret.append(HelpRangeCmdArgs()); |
1876 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
1877 | 0 | ret.append(" [--" + ARG_TIMESTAMP + "]"); |
1878 | 0 | ret.append(" [--" + ARG_MAX_KEYS + "=<N>q] "); |
1879 | 0 | ret.append(" [--" + ARG_TTL_START + "=<N>:- is inclusive]"); |
1880 | 0 | ret.append(" [--" + ARG_TTL_END + "=<N>:- is exclusive]"); |
1881 | 0 | ret.append(" [--" + ARG_NO_VALUE + "]"); |
1882 | 0 | ret.append(" [--" + ARG_ONLY_VERIFY_CHECKSUMS + "]"); |
1883 | 0 | ret.append("\n"); |
1884 | 0 | } |
1885 | | |
1886 | 2 | void ScanCommand::DoCommand() { |
1887 | 2 | if (!db_) { |
1888 | 0 | assert(GetExecuteState().IsFailed()); |
1889 | 0 | return; |
1890 | 0 | } |
1891 | | |
1892 | 2 | int num_keys_scanned = 0; |
1893 | 2 | Iterator* it = db_->NewIterator(ReadOptions(), GetCfHandle()); |
1894 | 2 | if (start_key_specified_) { |
1895 | 0 | it->Seek(start_key_); |
1896 | 2 | } else { |
1897 | 2 | it->SeekToFirst(); |
1898 | 2 | } |
1899 | 2 | int ttl_start; |
1900 | 2 | if (!ParseIntOption(option_map_, ARG_TTL_START, ttl_start, exec_state_)) { |
1901 | 2 | ttl_start = DBWithTTLImpl::kMinTimestamp; // TTL introduction time |
1902 | 2 | } |
1903 | 2 | int ttl_end; |
1904 | 2 | if (!ParseIntOption(option_map_, ARG_TTL_END, ttl_end, exec_state_)) { |
1905 | 2 | ttl_end = DBWithTTLImpl::kMaxTimestamp; // Max time allowed by TTL feature |
1906 | 2 | } |
1907 | 2 | if (ttl_end < ttl_start) { |
1908 | 0 | fprintf(stderr, "Error: End time can't be less than start time\n"); |
1909 | 0 | delete it; |
1910 | 0 | return; |
1911 | 0 | } |
1912 | 2 | if (is_db_ttl_ && timestamp_) { |
1913 | 0 | fprintf(stdout, "Scanning key-values from %s to %s\n", |
1914 | 0 | ReadableTime(ttl_start).c_str(), ReadableTime(ttl_end).c_str()); |
1915 | 0 | } |
1916 | 2 | for ( ; |
1917 | 6 | it->Valid() && (!end_key_specified_ || it->key().ToString() < end_key_); |
1918 | 4 | it->Next()) { |
1919 | 4 | if (is_db_ttl_) { |
1920 | 0 | TtlIterator* it_ttl = dynamic_cast<TtlIterator*>(it); |
1921 | 0 | assert(it_ttl); |
1922 | 0 | int rawtime = it_ttl->timestamp(); |
1923 | 0 | if (rawtime < ttl_start || rawtime >= ttl_end) { |
1924 | 0 | continue; |
1925 | 0 | } |
1926 | 0 | if (timestamp_ && !only_verify_checksums_) { |
1927 | 0 | fprintf(stdout, "%s ", ReadableTime(rawtime).c_str()); |
1928 | 0 | } |
1929 | 0 | } |
1930 | | |
1931 | 4 | Slice key_slice = it->key(); |
1932 | | |
1933 | 4 | std::string formatted_key; |
1934 | 4 | if (is_key_hex_) { |
1935 | 0 | formatted_key = "0x" + key_slice.ToString(true /* hex */); |
1936 | 0 | key_slice = formatted_key; |
1937 | 4 | } else if (ldb_options_.key_formatter) { |
1938 | 0 | formatted_key = ldb_options_.key_formatter->Format(key_slice); |
1939 | 0 | key_slice = formatted_key; |
1940 | 0 | } |
1941 | | |
1942 | 4 | if (no_value_ && !only_verify_checksums_) { |
1943 | 0 | fprintf(stdout, "%.*s\n", static_cast<int>(key_slice.size()), |
1944 | 0 | key_slice.data()); |
1945 | 4 | } else { |
1946 | 4 | Slice val_slice = it->value(); |
1947 | 4 | std::string formatted_value; |
1948 | 4 | if (is_value_hex_) { |
1949 | 0 | formatted_value = "0x" + val_slice.ToString(true /* hex */); |
1950 | 0 | val_slice = formatted_value; |
1951 | 0 | } |
1952 | 4 | if (!only_verify_checksums_) { |
1953 | 0 | fprintf(stdout, "%.*s : %.*s\n", static_cast<int>(key_slice.size()), |
1954 | 0 | key_slice.data(), static_cast<int>(val_slice.size()), |
1955 | 0 | val_slice.data()); |
1956 | 0 | } |
1957 | 4 | } |
1958 | | |
1959 | 4 | num_keys_scanned++; |
1960 | 4 | if (max_keys_scanned_ >= 0 && num_keys_scanned >= max_keys_scanned_) { |
1961 | 0 | break; |
1962 | 0 | } |
1963 | 4 | } |
1964 | 2 | if (!it->status().ok()) { // Check for any errors found during the scan |
1965 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(it->status().ToString()); |
1966 | 0 | } |
1967 | 2 | delete it; |
1968 | 2 | } |
1969 | | |
1970 | | // ---------------------------------------------------------------------------- |
1971 | | |
1972 | | DeleteCommand::DeleteCommand(const vector<string>& params, |
1973 | | const map<string, string>& options, const vector<string>& flags) : |
1974 | | LDBCommand(options, flags, false, |
1975 | 0 | BuildCmdLineOptions({ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX})) { |
1976 | |
|
1977 | 0 | if (params.size() != 1) { |
1978 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
1979 | 0 | "KEY must be specified for the delete command"); |
1980 | 0 | } else { |
1981 | 0 | key_ = params.at(0); |
1982 | 0 | if (is_key_hex_) { |
1983 | 0 | key_ = HexToString(key_); |
1984 | 0 | } |
1985 | 0 | } |
1986 | 0 | } |
1987 | | |
1988 | 0 | void DeleteCommand::Help(string& ret) { |
1989 | 0 | ret.append(" "); |
1990 | 0 | ret.append(DeleteCommand::Name() + " <key>"); |
1991 | 0 | ret.append("\n"); |
1992 | 0 | } |
1993 | | |
1994 | 0 | void DeleteCommand::DoCommand() { |
1995 | 0 | if (!db_) { |
1996 | 0 | assert(GetExecuteState().IsFailed()); |
1997 | 0 | return; |
1998 | 0 | } |
1999 | 0 | Status st = db_->Delete(WriteOptions(), GetCfHandle(), key_); |
2000 | 0 | if (st.ok()) { |
2001 | 0 | fprintf(stdout, "OK\n"); |
2002 | 0 | } else { |
2003 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
2004 | 0 | } |
2005 | 0 | } |
2006 | | |
2007 | | |
2008 | | PutCommand::PutCommand(const vector<string>& params, |
2009 | | const map<string, string>& options, const vector<string>& flags) : |
2010 | | LDBCommand(options, flags, false, |
2011 | | BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, |
2012 | 0 | ARG_CREATE_IF_MISSING})) { |
2013 | |
|
2014 | 0 | if (params.size() != 2) { |
2015 | 0 | exec_state_ = LDBCommandExecuteResult::Failed( |
2016 | 0 | "<key> and <value> must be specified for the put command"); |
2017 | 0 | } else { |
2018 | 0 | key_ = params.at(0); |
2019 | 0 | value_ = params.at(1); |
2020 | 0 | } |
2021 | |
|
2022 | 0 | if (is_key_hex_) { |
2023 | 0 | key_ = HexToString(key_); |
2024 | 0 | } |
2025 | |
|
2026 | 0 | if (is_value_hex_) { |
2027 | 0 | value_ = HexToString(value_); |
2028 | 0 | } |
2029 | 0 | } |
2030 | | |
2031 | 0 | void PutCommand::Help(string& ret) { |
2032 | 0 | ret.append(" "); |
2033 | 0 | ret.append(PutCommand::Name()); |
2034 | 0 | ret.append(" <key> <value> "); |
2035 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
2036 | 0 | ret.append("\n"); |
2037 | 0 | } |
2038 | | |
2039 | 0 | void PutCommand::DoCommand() { |
2040 | 0 | if (!db_) { |
2041 | 0 | assert(GetExecuteState().IsFailed()); |
2042 | 0 | return; |
2043 | 0 | } |
2044 | 0 | Status st = db_->Put(WriteOptions(), GetCfHandle(), key_, value_); |
2045 | 0 | if (st.ok()) { |
2046 | 0 | fprintf(stdout, "OK\n"); |
2047 | 0 | } else { |
2048 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
2049 | 0 | } |
2050 | 0 | } |
2051 | | |
2052 | 0 | Options PutCommand::PrepareOptionsForOpenDB() { |
2053 | 0 | Options opt = LDBCommand::PrepareOptionsForOpenDB(); |
2054 | 0 | opt.create_if_missing = IsFlagPresent(flags_, ARG_CREATE_IF_MISSING); |
2055 | 0 | return opt; |
2056 | 0 | } |
2057 | | |
2058 | | // ---------------------------------------------------------------------------- |
2059 | | |
2060 | | const char* DBQuerierCommand::HELP_CMD = "help"; |
2061 | | const char* DBQuerierCommand::GET_CMD = "get"; |
2062 | | const char* DBQuerierCommand::PUT_CMD = "put"; |
2063 | | const char* DBQuerierCommand::DELETE_CMD = "delete"; |
2064 | | |
2065 | | DBQuerierCommand::DBQuerierCommand(const vector<string>& params, |
2066 | | const map<string, string>& options, const vector<string>& flags) : |
2067 | | LDBCommand(options, flags, false, |
2068 | | BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX, |
2069 | 0 | ARG_VALUE_HEX})) { |
2070 | |
|
2071 | 0 | } |
2072 | | |
2073 | 0 | void DBQuerierCommand::Help(string& ret) { |
2074 | 0 | ret.append(" "); |
2075 | 0 | ret.append(DBQuerierCommand::Name()); |
2076 | 0 | ret.append(" [--" + ARG_TTL + "]"); |
2077 | 0 | ret.append("\n"); |
2078 | 0 | ret.append(" Starts a REPL shell. Type help for list of available " |
2079 | 0 | "commands."); |
2080 | 0 | ret.append("\n"); |
2081 | 0 | } |
2082 | | |
2083 | 0 | void DBQuerierCommand::DoCommand() { |
2084 | 0 | if (!db_) { |
2085 | 0 | assert(GetExecuteState().IsFailed()); |
2086 | 0 | return; |
2087 | 0 | } |
2088 | | |
2089 | 0 | ReadOptions read_options; |
2090 | 0 | WriteOptions write_options; |
2091 | |
|
2092 | 0 | string line; |
2093 | 0 | string key; |
2094 | 0 | string value; |
2095 | 0 | while (getline(std::cin, line, '\n')) { |
2096 | | |
2097 | | // Parse line into vector<string> |
2098 | 0 | vector<string> tokens; |
2099 | 0 | size_t pos = 0; |
2100 | 0 | while (true) { |
2101 | 0 | size_t pos2 = line.find(' ', pos); |
2102 | 0 | if (pos2 == string::npos) { |
2103 | 0 | break; |
2104 | 0 | } |
2105 | 0 | tokens.push_back(line.substr(pos, pos2-pos)); |
2106 | 0 | pos = pos2 + 1; |
2107 | 0 | } |
2108 | 0 | tokens.push_back(line.substr(pos)); |
2109 | |
|
2110 | 0 | const string& cmd = tokens[0]; |
2111 | |
|
2112 | 0 | if (cmd == HELP_CMD) { |
2113 | 0 | fprintf(stdout, |
2114 | 0 | "get <key>\n" |
2115 | 0 | "put <key> <value>\n" |
2116 | 0 | "delete <key>\n"); |
2117 | 0 | } else if (cmd == DELETE_CMD && tokens.size() == 2) { |
2118 | 0 | key = (is_key_hex_ ? HexToString(tokens[1]) : tokens[1]); |
2119 | 0 | CHECK_OK(db_->Delete(write_options, GetCfHandle(), Slice(key))); |
2120 | 0 | fprintf(stdout, "Successfully deleted %s\n", tokens[1].c_str()); |
2121 | 0 | } else if (cmd == PUT_CMD && tokens.size() == 3) { |
2122 | 0 | key = (is_key_hex_ ? HexToString(tokens[1]) : tokens[1]); |
2123 | 0 | value = (is_value_hex_ ? HexToString(tokens[2]) : tokens[2]); |
2124 | 0 | CHECK_OK(db_->Put(write_options, GetCfHandle(), Slice(key), Slice(value))); |
2125 | 0 | fprintf(stdout, "Successfully put %s %s\n", |
2126 | 0 | tokens[1].c_str(), tokens[2].c_str()); |
2127 | 0 | } else if (cmd == GET_CMD && tokens.size() == 2) { |
2128 | 0 | key = (is_key_hex_ ? HexToString(tokens[1]) : tokens[1]); |
2129 | 0 | if (db_->Get(read_options, GetCfHandle(), Slice(key), &value).ok()) { |
2130 | 0 | fprintf(stdout, "%s\n", PrintKeyValue(key, value, |
2131 | 0 | is_key_hex_, is_value_hex_).c_str()); |
2132 | 0 | } else { |
2133 | 0 | fprintf(stdout, "Not found %s\n", tokens[1].c_str()); |
2134 | 0 | } |
2135 | 0 | } else { |
2136 | 0 | fprintf(stdout, "Unknown command %s\n", line.c_str()); |
2137 | 0 | } |
2138 | 0 | } |
2139 | 0 | } |
2140 | | |
2141 | | // ---------------------------------------------------------------------------- |
2142 | | |
2143 | | CheckConsistencyCommand::CheckConsistencyCommand(const vector<string>& params, |
2144 | | const map<string, string>& options, const vector<string>& flags) : |
2145 | | LDBCommand(options, flags, false, |
2146 | 2 | BuildCmdLineOptions({})) { |
2147 | 2 | } |
2148 | | |
2149 | 0 | void CheckConsistencyCommand::Help(string& ret) { |
2150 | 0 | ret.append(" "); |
2151 | 0 | ret.append(CheckConsistencyCommand::Name()); |
2152 | 0 | ret.append("\n"); |
2153 | 0 | } |
2154 | | |
2155 | 2 | void CheckConsistencyCommand::DoCommand() { |
2156 | 2 | Options opt = PrepareOptionsForOpenDB(); |
2157 | 2 | opt.paranoid_checks = true; |
2158 | 2 | if (!exec_state_.IsNotStarted()) { |
2159 | 0 | return; |
2160 | 0 | } |
2161 | 2 | DB* db; |
2162 | 2 | Status st = DB::OpenForReadOnly(opt, db_path_, &db, false); |
2163 | 2 | delete db; |
2164 | 2 | if (st.ok()) { |
2165 | 2 | fprintf(stdout, "OK\n"); |
2166 | 0 | } else { |
2167 | 0 | exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); |
2168 | 0 | } |
2169 | 2 | } |
2170 | | |
2171 | | // ---------------------------------------------------------------------------- |
2172 | | |
2173 | | namespace { |
2174 | | |
2175 | 0 | void DumpSstFile(std::string filename, bool output_hex, bool show_properties) { |
2176 | 0 | std::string from_key; |
2177 | 0 | std::string to_key; |
2178 | 0 | if (filename.length() <= 4 || |
2179 | 0 | filename.rfind(".sst") != filename.length() - 4) { |
2180 | 0 | std::cout << "Invalid sst file name." << std::endl; |
2181 | 0 | return; |
2182 | 0 | } |
2183 | | // no verification |
2184 | 0 | rocksdb::SstFileReader reader( |
2185 | 0 | filename, false, (output_hex ? OutputFormat::kHex : OutputFormat::kRaw)); |
2186 | 0 | Status st = reader.ReadSequential(true, -1, false, // has_from |
2187 | 0 | from_key, false, // has_to |
2188 | 0 | to_key); |
2189 | 0 | if (!st.ok()) { |
2190 | 0 | std::cerr << "Error in reading SST file " << filename << st.ToString() |
2191 | 0 | << std::endl; |
2192 | 0 | return; |
2193 | 0 | } |
2194 | | |
2195 | 0 | if (show_properties) { |
2196 | 0 | const rocksdb::TableProperties* table_properties; |
2197 | |
|
2198 | 0 | std::shared_ptr<const rocksdb::TableProperties> |
2199 | 0 | table_properties_from_reader; |
2200 | 0 | st = reader.ReadTableProperties(&table_properties_from_reader); |
2201 | 0 | if (!st.ok()) { |
2202 | 0 | std::cerr << filename << ": " << st.ToString() |
2203 | 0 | << ". Try to use initial table properties" << std::endl; |
2204 | 0 | table_properties = reader.GetInitTableProperties(); |
2205 | 0 | } else { |
2206 | 0 | table_properties = table_properties_from_reader.get(); |
2207 | 0 | } |
2208 | 0 | if (table_properties != nullptr) { |
2209 | 0 | std::cout << std::endl << "Table Properties:" << std::endl; |
2210 | 0 | std::cout << table_properties->ToString("\n") << std::endl; |
2211 | 0 | std::cout << "# deleted keys: " |
2212 | 0 | << rocksdb::GetDeletedKeys( |
2213 | 0 | table_properties->user_collected_properties) |
2214 | 0 | << std::endl; |
2215 | 0 | } |
2216 | 0 | } |
2217 | 0 | } |
2218 | | |
2219 | | } // namespace |
2220 | | |
2221 | | DBFileDumperCommand::DBFileDumperCommand(const vector<string>& params, |
2222 | | const map<string, string>& options, |
2223 | | const vector<string>& flags) |
2224 | 0 | : LDBCommand(options, flags, true, BuildCmdLineOptions({})) {} |
2225 | | |
2226 | 0 | void DBFileDumperCommand::Help(string& ret) { |
2227 | 0 | ret.append(" "); |
2228 | 0 | ret.append(DBFileDumperCommand::Name()); |
2229 | 0 | ret.append("\n"); |
2230 | 0 | } |
2231 | | |
2232 | 0 | void DBFileDumperCommand::DoCommand() { |
2233 | 0 | if (!db_) { |
2234 | 0 | assert(GetExecuteState().IsFailed()); |
2235 | 0 | return; |
2236 | 0 | } |
2237 | 0 | Status s; |
2238 | |
|
2239 | 0 | std::cout << "Manifest File" << std::endl; |
2240 | 0 | std::cout << "==============================" << std::endl; |
2241 | 0 | std::string manifest_filename; |
2242 | 0 | s = ReadFileToString(db_->GetEnv(), CurrentFileName(db_->GetName()), |
2243 | 0 | &manifest_filename); |
2244 | 0 | if (!s.ok() || manifest_filename.empty() || |
2245 | 0 | manifest_filename.back() != '\n') { |
2246 | 0 | std::cerr << "Error when reading CURRENT file " |
2247 | 0 | << CurrentFileName(db_->GetName()) << std::endl; |
2248 | 0 | } |
2249 | | // remove the trailing '\n' |
2250 | 0 | manifest_filename.resize(manifest_filename.size() - 1); |
2251 | 0 | string manifest_filepath = db_->GetName() + "/" + manifest_filename; |
2252 | 0 | std::cout << manifest_filepath << std::endl; |
2253 | 0 | DumpManifestFile(manifest_filepath, false, false); |
2254 | 0 | std::cout << std::endl; |
2255 | |
|
2256 | 0 | std::cout << "SST Files" << std::endl; |
2257 | 0 | std::cout << "==============================" << std::endl; |
2258 | 0 | std::vector<LiveFileMetaData> metadata; |
2259 | 0 | db_->GetLiveFilesMetaData(&metadata); |
2260 | 0 | for (auto& fileMetadata : metadata) { |
2261 | 0 | std::string filename = fileMetadata.db_path + fileMetadata.name; |
2262 | 0 | std::cout << filename << " level:" << fileMetadata.level << std::endl; |
2263 | 0 | std::cout << "------------------------------" << std::endl; |
2264 | 0 | DumpSstFile(filename, false, true); |
2265 | 0 | std::cout << std::endl; |
2266 | 0 | } |
2267 | 0 | std::cout << std::endl; |
2268 | |
|
2269 | 0 | std::cout << "Write Ahead Log Files" << std::endl; |
2270 | 0 | std::cout << "==============================" << std::endl; |
2271 | 0 | rocksdb::VectorLogPtr wal_files; |
2272 | 0 | s = db_->GetSortedWalFiles(&wal_files); |
2273 | 0 | if (!s.ok()) { |
2274 | 0 | std::cerr << "Error when getting WAL files" << std::endl; |
2275 | 0 | } else { |
2276 | 0 | for (auto& wal : wal_files) { |
2277 | | // TODO(qyang): option.wal_dir should be passed into ldb command |
2278 | 0 | std::string filename = db_->GetOptions().wal_dir + wal->PathName(); |
2279 | 0 | std::cout << filename << std::endl; |
2280 | 0 | DumpWalFile(filename, true, true, &exec_state_); |
2281 | 0 | } |
2282 | 0 | } |
2283 | 0 | } |
2284 | | |
2285 | | } // namespace rocksdb |
2286 | | #endif // ROCKSDB_LITE |