Home | History | Annotate | Download | only in db
      1 // Copyright (c) 2012 The LevelDB Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
      4 
      5 #include <stdio.h>
      6 #include "db/dbformat.h"
      7 #include "db/filename.h"
      8 #include "db/log_reader.h"
      9 #include "db/version_edit.h"
     10 #include "db/write_batch_internal.h"
     11 #include "leveldb/env.h"
     12 #include "leveldb/iterator.h"
     13 #include "leveldb/options.h"
     14 #include "leveldb/status.h"
     15 #include "leveldb/table.h"
     16 #include "leveldb/write_batch.h"
     17 #include "util/logging.h"
     18 
     19 namespace leveldb {
     20 
     21 namespace {
     22 
     23 bool GuessType(const std::string& fname, FileType* type) {
     24   size_t pos = fname.rfind('/');
     25   std::string basename;
     26   if (pos == std::string::npos) {
     27     basename = fname;
     28   } else {
     29     basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1);
     30   }
     31   uint64_t ignored;
     32   return ParseFileName(basename, &ignored, type);
     33 }
     34 
     35 // Notified when log reader encounters corruption.
     36 class CorruptionReporter : public log::Reader::Reporter {
     37  public:
     38   virtual void Corruption(size_t bytes, const Status& status) {
     39     printf("corruption: %d bytes; %s\n",
     40             static_cast<int>(bytes),
     41             status.ToString().c_str());
     42   }
     43 };
     44 
     45 // Print contents of a log file. (*func)() is called on every record.
     46 bool PrintLogContents(Env* env, const std::string& fname,
     47                       void (*func)(Slice)) {
     48   SequentialFile* file;
     49   Status s = env->NewSequentialFile(fname, &file);
     50   if (!s.ok()) {
     51     fprintf(stderr, "%s\n", s.ToString().c_str());
     52     return false;
     53   }
     54   CorruptionReporter reporter;
     55   log::Reader reader(file, &reporter, true, 0);
     56   Slice record;
     57   std::string scratch;
     58   while (reader.ReadRecord(&record, &scratch)) {
     59     printf("--- offset %llu; ",
     60            static_cast<unsigned long long>(reader.LastRecordOffset()));
     61     (*func)(record);
     62   }
     63   delete file;
     64   return true;
     65 }
     66 
     67 // Called on every item found in a WriteBatch.
     68 class WriteBatchItemPrinter : public WriteBatch::Handler {
     69  public:
     70   uint64_t offset_;
     71   uint64_t sequence_;
     72 
     73   virtual void Put(const Slice& key, const Slice& value) {
     74     printf("  put '%s' '%s'\n",
     75            EscapeString(key).c_str(),
     76            EscapeString(value).c_str());
     77   }
     78   virtual void Delete(const Slice& key) {
     79     printf("  del '%s'\n",
     80            EscapeString(key).c_str());
     81   }
     82 };
     83 
     84 
     85 // Called on every log record (each one of which is a WriteBatch)
     86 // found in a kLogFile.
     87 static void WriteBatchPrinter(Slice record) {
     88   if (record.size() < 12) {
     89     printf("log record length %d is too small\n",
     90            static_cast<int>(record.size()));
     91     return;
     92   }
     93   WriteBatch batch;
     94   WriteBatchInternal::SetContents(&batch, record);
     95   printf("sequence %llu\n",
     96          static_cast<unsigned long long>(WriteBatchInternal::Sequence(&batch)));
     97   WriteBatchItemPrinter batch_item_printer;
     98   Status s = batch.Iterate(&batch_item_printer);
     99   if (!s.ok()) {
    100     printf("  error: %s\n", s.ToString().c_str());
    101   }
    102 }
    103 
    104 bool DumpLog(Env* env, const std::string& fname) {
    105   return PrintLogContents(env, fname, WriteBatchPrinter);
    106 }
    107 
    108 // Called on every log record (each one of which is a WriteBatch)
    109 // found in a kDescriptorFile.
    110 static void VersionEditPrinter(Slice record) {
    111   VersionEdit edit;
    112   Status s = edit.DecodeFrom(record);
    113   if (!s.ok()) {
    114     printf("%s\n", s.ToString().c_str());
    115     return;
    116   }
    117   printf("%s", edit.DebugString().c_str());
    118 }
    119 
    120 bool DumpDescriptor(Env* env, const std::string& fname) {
    121   return PrintLogContents(env, fname, VersionEditPrinter);
    122 }
    123 
    124 bool DumpTable(Env* env, const std::string& fname) {
    125   uint64_t file_size;
    126   RandomAccessFile* file = NULL;
    127   Table* table = NULL;
    128   Status s = env->GetFileSize(fname, &file_size);
    129   if (s.ok()) {
    130     s = env->NewRandomAccessFile(fname, &file);
    131   }
    132   if (s.ok()) {
    133     // We use the default comparator, which may or may not match the
    134     // comparator used in this database. However this should not cause
    135     // problems since we only use Table operations that do not require
    136     // any comparisons.  In particular, we do not call Seek or Prev.
    137     s = Table::Open(Options(), file, file_size, &table);
    138   }
    139   if (!s.ok()) {
    140     fprintf(stderr, "%s\n", s.ToString().c_str());
    141     delete table;
    142     delete file;
    143     return false;
    144   }
    145 
    146   ReadOptions ro;
    147   ro.fill_cache = false;
    148   Iterator* iter = table->NewIterator(ro);
    149   for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
    150     ParsedInternalKey key;
    151     if (!ParseInternalKey(iter->key(), &key)) {
    152       printf("badkey '%s' => '%s'\n",
    153              EscapeString(iter->key()).c_str(),
    154              EscapeString(iter->value()).c_str());
    155     } else {
    156       char kbuf[20];
    157       const char* type;
    158       if (key.type == kTypeDeletion) {
    159         type = "del";
    160       } else if (key.type == kTypeValue) {
    161         type = "val";
    162       } else {
    163         snprintf(kbuf, sizeof(kbuf), "%d", static_cast<int>(key.type));
    164         type = kbuf;
    165       }
    166       printf("'%s' @ %8llu : %s => '%s'\n",
    167              EscapeString(key.user_key).c_str(),
    168              static_cast<unsigned long long>(key.sequence),
    169              type,
    170              EscapeString(iter->value()).c_str());
    171     }
    172   }
    173   s = iter->status();
    174   if (!s.ok()) {
    175     printf("iterator error: %s\n", s.ToString().c_str());
    176   }
    177 
    178   delete iter;
    179   delete table;
    180   delete file;
    181   return true;
    182 }
    183 
    184 bool DumpFile(Env* env, const std::string& fname) {
    185   FileType ftype;
    186   if (!GuessType(fname, &ftype)) {
    187     fprintf(stderr, "%s: unknown file type\n", fname.c_str());
    188     return false;
    189   }
    190   switch (ftype) {
    191     case kLogFile:         return DumpLog(env, fname);
    192     case kDescriptorFile:  return DumpDescriptor(env, fname);
    193     case kTableFile:       return DumpTable(env, fname);
    194 
    195     default: {
    196       fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str());
    197       break;
    198     }
    199   }
    200   return false;
    201 }
    202 
    203 bool HandleDumpCommand(Env* env, char** files, int num) {
    204   bool ok = true;
    205   for (int i = 0; i < num; i++) {
    206     ok &= DumpFile(env, files[i]);
    207   }
    208   return ok;
    209 }
    210 
    211 }
    212 }  // namespace leveldb
    213 
    214 static void Usage() {
    215   fprintf(
    216       stderr,
    217       "Usage: leveldbutil command...\n"
    218       "   dump files...         -- dump contents of specified files\n"
    219       );
    220 }
    221 
    222 int main(int argc, char** argv) {
    223   leveldb::Env* env = leveldb::Env::Default();
    224   bool ok = true;
    225   if (argc < 2) {
    226     Usage();
    227     ok = false;
    228   } else {
    229     std::string command = argv[1];
    230     if (command == "dump") {
    231       ok = leveldb::HandleDumpCommand(env, argv+2, argc-2);
    232     } else {
    233       Usage();
    234       ok = false;
    235     }
    236   }
    237   return (ok ? 0 : 1);
    238 }
    239