xref: /6.6.0/forestdb/tools/forestdb_dump.cc (revision 664c0dd5)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2010 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include "dump_common.h"
19
20void print_usage(void)
21{
22    printf("\nUsage: forestdb_dump [OPTION]... [filename]\n"
23    "\nOptions:\n"
24    "\n      --header-only         only print the header of a given ForestDB file"
25    "\n      --key <key>           dump only specified document"
26    "\n      --kvs <KV store name> name of KV store to be dumped"
27    "\n      --byid                sort output by document id"
28    "\n      --byseq               sort output by sequence number"
29    "\n      --hex-key             convert document id to hex (for binary key)"
30    "\n      --hex-body            convert document body data to hex (for binary data)"
31    "\n      --hex-align           number of bytes of hex alignment (default 16)"
32    "\n      --plain-meta          print meta data in plain text (default hex)"
33    "\n      --no-body             do not retrieve document bodies"
34    "\n      --no-meta             do not print meta data of documents"
35    "\n");
36}
37
38typedef enum  {
39    SCAN_BY_KEY = 1,
40    SCAN_BY_SEQ = 2,
41} scan_mode_t;
42
43struct dump_option{
44    char *dump_file;
45    char *one_key;
46    char *one_kvs;
47    int hex_align;
48    bool no_body;
49    bool no_meta;
50    bool print_key_in_hex;
51    bool print_plain_meta;
52    bool print_body_in_hex;
53    bool print_header_only;
54    scan_mode_t scan_mode;
55};
56
57INLINE void print_buf(fdb_kvs_handle *db, void *buf, size_t buflen, bool hex,
58                      int align)
59{
60    if (buf) {
61        if (!hex) {
62            // plaintext
63            printf("%.*s\n", (int)buflen, (char*)buf);
64        } else {
65            // hex dump
66            size_t i, j;
67            printf("(hex)\n");
68            for (i=0;i<buflen;i+=align) {
69                printf("        ");
70                for (j=i; j<i+align; ++j){
71                    if (j<buflen) {
72                        printf("%02x ", ((uint8_t*)buf)[j]);
73                    } else {
74                        printf("   ");
75                    }
76                    if ((j+1)%8 == 0) {
77                        printf(" ");
78                    }
79                }
80                printf(" ");
81                for (j=i; j<i+align && j<buflen; ++j){
82                    // print only readable ascii character
83                    printf("%c",
84                     (0x20 <= ((char*)buf)[j] && ((char*)buf)[j] <= 0x7d)?
85                               ((char*)buf)[j] : '.'  );
86                }
87                printf("\n");
88            }
89        }
90    } else {
91        printf("(null)\n");
92    }
93}
94
95void print_doc(fdb_kvs_handle *db,
96               char *kvs_name,
97               uint64_t offset,
98               struct dump_option *opt)
99{
100    uint8_t is_wal_entry;
101    int64_t _offset;
102    void *key;
103    keylen_t keylen;
104    fdb_status wr;
105    fdb_doc fdoc;
106    struct docio_object doc;
107    struct _fdb_key_cmp_info cmp_info;
108
109    memset(&doc, 0, sizeof(struct docio_object));
110
111    _offset = docio_read_doc(db->dhandle, offset, &doc, true);
112    if (_offset <= 0) {
113        return;
114    }
115    if (doc.length.flag & DOCIO_TXN_COMMITTED) {
116        offset = doc.doc_offset;
117        _offset = docio_read_doc(db->dhandle, offset, &doc, true);
118        if (_offset <= 0) {
119            return;
120        }
121    }
122
123    if (db->kvs) {
124        key = (uint8_t*)doc.key + sizeof(fdb_kvs_id_t);
125        keylen = doc.length.keylen - sizeof(fdb_kvs_id_t);
126    } else {
127        key = doc.key;
128        keylen = doc.length.keylen;
129    }
130
131    printf("Doc ID: ");
132    print_buf(db, key, keylen,
133              opt->print_key_in_hex, opt->hex_align);
134    if (kvs_name) {
135        printf("    KV store name: %s\n", kvs_name);
136    }
137    if (doc.seqnum != SEQNUM_NOT_USED) {
138        printf("    Sequence number: %" _F64 "\n", doc.seqnum);
139    }
140    printf("    Byte offset: %" _F64 "\n", offset);
141
142    cmp_info.kvs_config = db->kvs_config;
143    cmp_info.kvs = db->kvs;
144    fdoc.key = doc.key;
145    fdoc.keylen = doc.length.keylen;
146    wr = wal_find(&db->file->global_txn, db->file,
147                  &cmp_info, db->shandle, &fdoc, &offset);
148    is_wal_entry = (wr == FDB_RESULT_SUCCESS)?(1):(0);
149    printf("    Indexed by %s\n", (is_wal_entry)?("WAL"):("the main index"));
150    printf("    Length: %d (key), %d (metadata), %d (body)\n",
151           keylen, doc.length.metalen, doc.length.bodylen);
152    if (doc.length.flag & DOCIO_COMPRESSED) {
153        printf("    Compressed body size on disk: %d\n",
154               doc.length.bodylen_ondisk);
155    }
156    if (doc.length.flag & DOCIO_DELETED) {
157        printf("    Status: deleted (timestamp: %u)\n", doc.timestamp);
158    } else {
159        if (doc.length.flag & DOCIO_COMPACT) {
160            printf("    Status: normal (written during compaction)\n");
161        } else {
162            printf("    Status: normal\n");
163        }
164    }
165    if (!opt->no_meta) {
166        printf("    Metadata: ");
167        print_buf(db, doc.meta, doc.length.metalen,
168                  !opt->print_plain_meta, opt->hex_align);
169    }
170    if (!opt->no_body) {
171        printf("    Body: ");
172        print_buf(db, doc.body, doc.length.bodylen,
173                  opt->print_body_in_hex, opt->hex_align);
174    }
175    printf("\n");
176
177    free(doc.key);
178    free(doc.meta);
179    free(doc.body);
180}
181
182int scan_docs(fdb_kvs_handle *db, struct dump_option *opt, char *kvs_name)
183{
184    uint64_t offset;
185    fdb_iterator *fit;
186    fdb_status fs;
187    fdb_doc *fdoc = NULL;
188
189    if (opt->one_key) {
190        fdb_doc_create(&fdoc, opt->one_key,
191                       strnlen(opt->one_key,FDB_MAX_KEYLEN), NULL, 0, NULL, 0);
192       fs = fdb_get(db, fdoc);
193       if (fs == FDB_RESULT_SUCCESS) {
194           offset = fdoc->offset;
195           print_doc(db, kvs_name, offset, opt);
196       } else { // MB-22046: Also need to be able to print deleted doc
197           fs = fdb_get_metaonly(db, fdoc);
198           if (fs == FDB_RESULT_SUCCESS) {
199               offset = fdoc->offset;
200               print_doc(db, kvs_name, offset, opt);
201           } else {
202               return -1;
203           }
204       }
205       fdb_doc_free(fdoc);
206       fdoc = NULL;
207    } else if (opt->scan_mode == SCAN_BY_KEY) {
208        fs = fdb_iterator_init(db, &fit, NULL, 0, NULL, 0, 0x0);
209        if (fs != FDB_RESULT_SUCCESS) {
210            return -2;
211        }
212        do {
213            if (fdb_iterator_get(fit, &fdoc) == FDB_RESULT_SUCCESS) {
214                offset = fdoc->offset;
215                print_doc(db, kvs_name, offset, opt);
216                fdb_doc_free(fdoc);
217                fdoc = NULL;
218            }
219        } while(fdb_iterator_next(fit) == FDB_RESULT_SUCCESS);
220        fdb_iterator_close(fit);
221    } else if (opt->scan_mode == SCAN_BY_SEQ) {
222        fs = fdb_iterator_sequence_init(db, &fit, 0, -1, 0x0);
223        if (fs != FDB_RESULT_SUCCESS) {
224            return -2;
225        }
226        do {
227            if (fdb_iterator_get(fit, &fdoc) == FDB_RESULT_SUCCESS) {
228                offset = fdoc->offset;
229                print_doc(db, kvs_name, offset, opt);
230                fdb_doc_free(fdoc);
231                fdoc = NULL;
232            }
233        } while(fdb_iterator_next(fit) == FDB_RESULT_SUCCESS);
234        fdb_iterator_close(fit);
235    }
236
237    return 0;
238}
239
240int process_file(struct dump_option *opt)
241{
242    int i, ret;
243    fdb_file_handle *dbfile;
244    fdb_kvs_handle *db;
245    fdb_config config;
246    fdb_kvs_config kvs_config;
247    fdb_kvs_name_list name_list;
248    fdb_status fs;
249    char *filename = opt->dump_file;
250
251    config = fdb_get_default_config();
252    config.buffercache_size = 0;
253    config.flags = FDB_OPEN_FLAG_RDONLY;
254    fs = fdb_open(&dbfile, filename, &config);
255    if (fs != FDB_RESULT_SUCCESS) {
256        printf("\nUnable to open %s\n", filename);
257        return -3;
258    }
259    if (!opt->one_key && !opt->one_kvs) {
260        // MB-22046: Avoid dumping header for specific kvs or specific key dump
261        print_header(dbfile->root);
262        if (opt->print_header_only) {
263            return 0;
264        }
265    }
266
267    kvs_config = fdb_get_default_kvs_config();
268
269    if (dbfile->root->config.multi_kv_instances) {
270        fdb_get_kvs_name_list(dbfile, &name_list);
271        for (i=0; (uint64_t)i<name_list.num_kvs_names; ++i) {
272            if (opt->one_kvs &&
273                strcmp(opt->one_kvs, name_list.kvs_names[i])) {
274                continue;
275            }
276
277            fs = fdb_kvs_open(dbfile, &db, name_list.kvs_names[i], &kvs_config);
278            if (fs != FDB_RESULT_SUCCESS) {
279                printf("\nUnable to open KV store %s\n", name_list.kvs_names[i]);
280                continue;
281            }
282            if (db->kvs_config.custom_cmp) {
283                printf("\nUnable to dump KV store %s due to "
284                       "customized comparison function\n", name_list.kvs_names[i]);
285                fdb_kvs_close(db);
286                continue;
287            }
288
289            ret = scan_docs(db, opt, name_list.kvs_names[i]);
290            if (ret == -1 && opt->one_kvs) {
291                // Only print key not found if a specific key is accompanied by
292                // a specific kv store.
293                // Otherwise scan all kv stores for the same key..
294                printf("KV store '%s': key not found\n", name_list.kvs_names[i]);
295            }
296            fdb_kvs_close(db);
297        }
298
299        fdb_free_kvs_name_list(&name_list);
300    } else {
301        fs = fdb_kvs_open(dbfile, &db, NULL, &kvs_config);
302        if (fs != FDB_RESULT_SUCCESS) {
303            printf("\nUnable to open KV store\n");
304            return -3;
305        }
306
307        printf("\n");
308        ret = scan_docs(db, opt, NULL);
309        if (ret == -1) {
310            printf("Key not found\n");
311        }
312        fdb_kvs_close(db);
313    }
314
315    fs = fdb_close(dbfile);
316    if (fs != FDB_RESULT_SUCCESS) {
317        printf("\nUnable to close %s\n", filename);
318        return -4;
319    }
320
321    fdb_shutdown();
322    return 0;
323}
324
325int parse_options(int argc, char **argv, struct dump_option *opt)
326{
327    // Unfortunately, we cannot use getopt generally
328    // because Windows doesn't support it ..
329    int i = 1;
330
331    if (argc < 2) {
332        print_usage();
333        return -1;
334    }
335
336    // load default options ...
337    memset(opt, 0, sizeof(struct dump_option));
338    opt->hex_align = 16;
339    opt->scan_mode = SCAN_BY_KEY;
340
341    for (i = 1; i < argc; ++i) {
342        if (argv[i][0] == '-' && argv[i][1] == '-') {
343            if (strncmp(argv[i], "--key", 16) == 0) {
344                opt->one_key = argv[++i];
345            } else if (strncmp(argv[i], "--kvs", 16) == 0) {
346                opt->one_kvs = argv[++i];
347            } else if (strncmp(argv[i], "--no-body", 16) == 0) {
348                opt->no_body = true;
349            } else if (strncmp(argv[i], "--no-meta", 16) == 0) {
350                opt->no_meta = true;
351            } else if (strncmp(argv[i], "--hex-key", 16) == 0) {
352                opt->print_key_in_hex = true;
353            } else if (strncmp(argv[i], "--plain-meta", 16) == 0) {
354                opt->print_plain_meta = true;
355            } else if (strncmp(argv[i], "--hex-body", 16) == 0) {
356                opt->print_body_in_hex = true;
357            } else if (strncmp(argv[i], "--hex-align", 16) == 0) {
358                opt->hex_align = atoi(argv[++i]);
359            } else if (strncmp(argv[i], "--byid", 16) == 0) {
360                opt->scan_mode = SCAN_BY_KEY;
361            } else if (strncmp(argv[i], "--byseq", 16) == 0) {
362                opt->scan_mode = SCAN_BY_SEQ;
363            } else if (strncmp(argv[i], "--header-only", 13) == 0) {
364                opt->print_header_only = true;
365            } else {
366                printf("\nUnknown option %s\n", argv[i]);
367                print_usage();
368                return -2;
369            }
370        } else {
371            opt->dump_file = argv[i];
372        }
373    }
374
375    return 0;
376}
377
378int main(int argc, char **argv)
379{
380    memleak_start();
381
382    struct dump_option opt;
383    int ret = parse_options(argc, argv, &opt);
384
385    if (ret) {
386        memleak_end();
387        return ret;
388    }
389
390    ret = process_file(&opt);
391
392    memleak_end();
393    return ret;
394}
395