func readFooter(r io.ReaderAt, size uint64) (mi, ii *bInfo, err error) { if size < uint64(footerSize) { err = errors.ErrInvalid("file is too short to be an sstable") return } buf := make([]byte, footerSize) n, err := r.ReadAt(buf, int64(size)-footerSize) if err != nil { return } if bytes.Compare(buf[handlesSize:], magicBytes) != 0 { err = errors.ErrInvalid("not an sstable (bad magic number)") return } mi = new(bInfo) n, err = mi.decodeFrom(buf) if err != nil { return } ii = new(bInfo) n, err = ii.decodeFrom(buf[n:]) if err != nil { return } return }
// GetProperty used to query exported database state. // // Valid property names include: // // "leveldb.num-files-at-level<N>" - return the number of files at level <N>, // where <N> is an ASCII representation of a level number (e.g. "0"). // "leveldb.stats" - returns a multi-line string that describes statistics // about the internal operation of the DB. // "leveldb.sstables" - returns a multi-line string that describes all // of the sstables that make up the db contents. func (d *DB) GetProperty(prop string) (value string, err error) { err = d.rok() if err != nil { return } const prefix = "leveldb." if !strings.HasPrefix(prop, prefix) { return "", errors.ErrInvalid("unknown property: " + prop) } p := prop[len(prefix):] switch s := d.s; true { case strings.HasPrefix(p, "num-files-at-level"): var level uint var rest string n, _ := fmt.Scanf("%d%s", &level, &rest) if n != 1 || level >= kNumLevels { return "", errors.ErrInvalid("invalid property: " + prop) } value = fmt.Sprint(s.version().tLen(int(level))) case p == "stats": v := s.version() value = "Compactions\n" + " Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" + "-------+------------+---------------+---------------+---------------+---------------\n" for level, tt := range v.tables { duration, read, write := d.cstats[level].get() if len(tt) == 0 && duration == 0 { continue } value += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", level, len(tt), float64(tt.size())/1048576.0, duration.Seconds(), float64(read)/1048576.0, float64(write)/1048576.0) } case p == "sstables": v := s.version() for level, tt := range v.tables { value += fmt.Sprintf("--- level %d ---\n", level) for _, t := range tt { value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.file.Num(), t.size, t.min, t.max) } } default: return "", errors.ErrInvalid("unknown property: " + prop) } return }
// Recover a database session; need external synchronization. func (s *session) recover() (err error) { file, err := s.stor.GetManifest() if err != nil { return } r, err := newJournalReader(file, true, s.journalDropFunc("manifest", file.Num())) if err != nil { return } defer r.close() cmp := s.cmp.cmp.Name() staging := s.version_NB().newStaging() srec := new(sessionRecord) for r.journal.Next() { rec := new(sessionRecord) err = rec.decode(r.journal.Record()) if err != nil { continue } if rec.hasComparer && rec.comparer != cmp { return errors.ErrInvalid("invalid comparer, " + "want '" + cmp + "', " + "got '" + rec.comparer + "'") } // save compact pointers for _, rp := range rec.compactPointers { s.stCPtrs[rp.level] = iKey(rp.key) } // commit record to version staging staging.commit(rec) if rec.hasJournalNum { srec.setJournalNum(rec.journalNum) } if rec.hasPrevJournalNum { srec.setPrevJournalNum(rec.prevJournalNum) } if rec.hasNextNum { srec.setNextNum(rec.nextNum) } if rec.hasSeq { srec.setSeq(rec.seq) } } // check for error in journal reader err = r.journal.Error() if err != nil { return } switch false { case srec.hasNextNum: err = errors.ErrCorrupt("manifest missing next file number") case srec.hasJournalNum: err = errors.ErrCorrupt("manifest missing journal file number") case srec.hasSeq: err = errors.ErrCorrupt("manifest missing seq number") } if err != nil { return } s.manifest = &journalWriter{file: file} s.setVersion(staging.finish()) s.setFileNum(srec.nextNum) s.recordCommited(srec) return }
func (p *sessionRecord) decodeFrom(r readByteReader) (err error) { for err == nil { var tag uint64 tag, err = binary.ReadUvarint(r) if err != nil { if err == io.EOF { err = nil } return } switch tag { case tagComparer: var cmp []byte cmp, err = readBytes(r) if err == nil { p.comparer = string(cmp) p.hasComparer = true } case tagLogNum: p.logNum, err = binary.ReadUvarint(r) if err == nil { p.hasLogNum = true } case tagPrevLogNum: err = errors.ErrInvalid("unsupported db format") break case tagNextNum: p.nextNum, err = binary.ReadUvarint(r) if err == nil { p.hasNextNum = true } case tagSeq: var seq uint64 seq, err = binary.ReadUvarint(r) if err == nil { p.seq = seq p.hasSeq = true } case tagCompactPointer: var level uint64 var b []byte level, err = binary.ReadUvarint(r) if err != nil { break } b, err = readBytes(r) if err != nil { break } p.addCompactPointer(int(level), b) case tagNewTable: var level, num, size uint64 var b []byte level, err = binary.ReadUvarint(r) if err != nil { break } num, err = binary.ReadUvarint(r) if err != nil { break } size, err = binary.ReadUvarint(r) if err != nil { break } b, err = readBytes(r) if err != nil { break } min := iKey(b) b, err = readBytes(r) if err != nil { break } max := iKey(b) p.addTable(int(level), num, size, min, max) case tagDeletedTable: var level, num uint64 level, err = binary.ReadUvarint(r) if err != nil { break } num, err = binary.ReadUvarint(r) if err != nil { break } p.deleteTable(int(level), num) } } return }