github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/tool/sstable.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package tool 6 7 import ( 8 "fmt" 9 "sort" 10 "text/tabwriter" 11 12 "github.com/petermattis/pebble/internal/base" 13 "github.com/petermattis/pebble/sstable" 14 "github.com/petermattis/pebble/vfs" 15 "github.com/spf13/cobra" 16 ) 17 18 // sstableT implements sstable-level tools, including both configuration state 19 // and the commands themselves. 20 type sstableT struct { 21 Root *cobra.Command 22 Check *cobra.Command 23 Layout *cobra.Command 24 Properties *cobra.Command 25 Scan *cobra.Command 26 27 // Configuration and state. 28 opts *sstable.Options 29 comparers sstable.Comparers 30 mergers sstable.Mergers 31 dbNum uint64 32 33 // Flags. 34 fmtKey formatter 35 fmtValue formatter 36 start key 37 end key 38 verbose bool 39 } 40 41 func newSSTable( 42 opts *base.Options, comparers sstable.Comparers, mergers sstable.Mergers, 43 ) *sstableT { 44 s := &sstableT{ 45 opts: opts, 46 comparers: comparers, 47 mergers: mergers, 48 } 49 s.fmtKey.mustSet("quoted") 50 s.fmtValue.mustSet("[%x]") 51 52 s.Root = &cobra.Command{ 53 Use: "sstable", 54 Short: "sstable introspection tools", 55 } 56 s.Check = &cobra.Command{ 57 Use: "check <sstables>", 58 Short: "verify checksums and metadata", 59 Long: ``, 60 Args: cobra.MinimumNArgs(1), 61 Run: s.runCheck, 62 } 63 s.Layout = &cobra.Command{ 64 Use: "layout <sstables>", 65 Short: "print sstable block and record layout", 66 Long: ` 67 Print the layout for the sstables. The -v flag controls whether record layout 68 is displayed or omitted. 69 `, 70 Args: cobra.MinimumNArgs(1), 71 Run: s.runLayout, 72 } 73 s.Properties = &cobra.Command{ 74 Use: "properties <sstables>", 75 Short: "print sstable properties", 76 Long: ` 77 Print the properties for the sstables. The -v flag controls whether the 78 properties are pretty-printed or displayed in a verbose/raw format. 79 `, 80 Args: cobra.MinimumNArgs(1), 81 Run: s.runProperties, 82 } 83 s.Scan = &cobra.Command{ 84 Use: "scan <sstables>", 85 Short: "print sstable records", 86 Long: ` 87 Print the records in the sstables. The sstables are scanned in command line 88 order which means the records will be printed in that order. 89 `, 90 Args: cobra.MinimumNArgs(1), 91 Run: s.runScan, 92 } 93 94 s.Root.AddCommand(s.Check, s.Layout, s.Properties, s.Scan) 95 for _, cmd := range []*cobra.Command{s.Layout, s.Properties} { 96 cmd.Flags().BoolVarP( 97 &s.verbose, "verbose", "v", false, 98 "verbose output") 99 } 100 101 s.Layout.Flags().Var( 102 &s.fmtKey, "key", "key formatter") 103 s.Layout.Flags().Var( 104 &s.fmtValue, "value", "value formatter") 105 s.Scan.Flags().Var( 106 &s.fmtKey, "key", "key formatter") 107 s.Scan.Flags().Var( 108 &s.fmtValue, "value", "value formatter") 109 s.Scan.Flags().Var( 110 &s.start, "start", "start key for the scan") 111 s.Scan.Flags().Var( 112 &s.end, "end", "end key for the scan") 113 114 return s 115 } 116 117 func (s *sstableT) newReader(f vfs.File) (*sstable.Reader, error) { 118 s.dbNum++ 119 return sstable.NewReader(f, s.dbNum, 0, s.opts, s.comparers, s.mergers) 120 } 121 122 func (s *sstableT) runCheck(cmd *cobra.Command, args []string) { 123 for _, arg := range args { 124 func() { 125 f, err := vfs.Default.Open(arg) 126 if err != nil { 127 fmt.Fprintf(stderr, "%s\n", err) 128 return 129 } 130 131 fmt.Fprintf(stdout, "%s\n", arg) 132 133 r, err := s.newReader(f) 134 defer r.Close() 135 136 if err != nil { 137 fmt.Fprintf(stdout, "%s\n", err) 138 return 139 } 140 141 iter := r.NewIter(nil, nil) 142 for key, _ := iter.First(); key != nil; key, _ = iter.Next() { 143 } 144 if err := iter.Close(); err != nil { 145 fmt.Fprintf(stdout, "%s\n", err) 146 } 147 }() 148 } 149 } 150 151 func (s *sstableT) runLayout(cmd *cobra.Command, args []string) { 152 for _, arg := range args { 153 func() { 154 f, err := vfs.Default.Open(arg) 155 if err != nil { 156 fmt.Fprintf(stderr, "%s\n", err) 157 return 158 } 159 160 fmt.Fprintf(stdout, "%s\n", arg) 161 162 r, err := s.newReader(f) 163 defer r.Close() 164 165 if err != nil { 166 fmt.Fprintf(stdout, "%s\n", err) 167 return 168 } 169 170 l, err := r.Layout() 171 if err != nil { 172 fmt.Fprintf(stderr, "%s\n", err) 173 return 174 } 175 fmtRecord := func(key *base.InternalKey, value []byte) { 176 formatKeyValue(stdout, s.fmtKey, s.fmtValue, key, value) 177 } 178 if s.fmtKey.spec == "null" && s.fmtValue.spec == "null" { 179 fmtRecord = nil 180 } 181 l.Describe(stdout, s.verbose, r, fmtRecord) 182 }() 183 } 184 } 185 186 func (s *sstableT) runProperties(cmd *cobra.Command, args []string) { 187 for _, arg := range args { 188 func() { 189 f, err := vfs.Default.Open(arg) 190 if err != nil { 191 fmt.Fprintf(stderr, "%s\n", err) 192 return 193 } 194 195 fmt.Fprintf(stdout, "%s\n", arg) 196 197 r, err := s.newReader(f) 198 defer r.Close() 199 200 if err != nil { 201 fmt.Fprintf(stdout, "%s\n", err) 202 return 203 } 204 205 if s.verbose { 206 fmt.Fprintf(stdout, "%s", r.Properties.String()) 207 return 208 } 209 210 stat, err := f.Stat() 211 if err != nil { 212 fmt.Fprintf(stderr, "%s\n", err) 213 return 214 } 215 216 formatNull := func(s string) string { 217 switch s { 218 case "", "nullptr": 219 return "-" 220 } 221 return s 222 } 223 224 tw := tabwriter.NewWriter(stdout, 2, 1, 2, ' ', 0) 225 fmt.Fprintf(tw, "version\t%d\n", r.Properties.Version) 226 fmt.Fprintf(tw, "size\t\n") 227 fmt.Fprintf(tw, " file\t%d\n", stat.Size()) 228 fmt.Fprintf(tw, " data\t%d\n", r.Properties.DataSize) 229 fmt.Fprintf(tw, " blocks\t%d\n", r.Properties.NumDataBlocks) 230 fmt.Fprintf(tw, " index\t%d\n", r.Properties.IndexSize) 231 fmt.Fprintf(tw, " blocks\t%d\n", 1+r.Properties.IndexPartitions) 232 fmt.Fprintf(tw, " top-level\t%d\n", r.Properties.TopLevelIndexSize) 233 fmt.Fprintf(tw, " filter\t%d\n", r.Properties.FilterSize) 234 fmt.Fprintf(tw, " raw-key\t%d\n", r.Properties.RawKeySize) 235 fmt.Fprintf(tw, " raw-value\t%d\n", r.Properties.RawValueSize) 236 fmt.Fprintf(tw, "records\t%d\n", r.Properties.NumEntries) 237 fmt.Fprintf(tw, " set\t%d\n", r.Properties.NumEntries- 238 (r.Properties.NumDeletions+r.Properties.NumRangeDeletions+r.Properties.NumMergeOperands)) 239 fmt.Fprintf(tw, " delete\t%d\n", r.Properties.NumDeletions) 240 fmt.Fprintf(tw, " range-delete\t%d\n", r.Properties.NumRangeDeletions) 241 fmt.Fprintf(tw, " merge\t%d\n", r.Properties.NumMergeOperands) 242 fmt.Fprintf(tw, " global-seq-num\t%d\n", r.Properties.GlobalSeqNum) 243 fmt.Fprintf(tw, "index\t\n") 244 fmt.Fprintf(tw, " key\t") 245 if r.Properties.IndexKeyIsUserKey != 0 { 246 fmt.Fprintf(tw, "user key\n") 247 } else { 248 fmt.Fprintf(tw, "internal key\n") 249 } 250 fmt.Fprintf(tw, " value\t") 251 if r.Properties.IndexValueIsDeltaEncoded != 0 { 252 fmt.Fprintf(tw, "delta encoded\n") 253 } else { 254 fmt.Fprintf(tw, "raw encoded\n") 255 } 256 fmt.Fprintf(tw, "comparer\t%s\n", r.Properties.ComparerName) 257 fmt.Fprintf(tw, "merger\t%s\n", formatNull(r.Properties.MergerName)) 258 fmt.Fprintf(tw, "filter\t%s\n", formatNull(r.Properties.FilterPolicyName)) 259 fmt.Fprintf(tw, " prefix\t%t\n", r.Properties.PrefixFiltering) 260 fmt.Fprintf(tw, " whole-key\t%t\n", r.Properties.WholeKeyFiltering) 261 fmt.Fprintf(tw, "compression\t%s\n", r.Properties.CompressionName) 262 fmt.Fprintf(tw, " options\t%s\n", r.Properties.CompressionOptions) 263 fmt.Fprintf(tw, "user properties\t\n") 264 fmt.Fprintf(tw, " collectors\t%s\n", r.Properties.PropertyCollectorNames) 265 keys := make([]string, 0, len(r.Properties.UserProperties)) 266 for key := range r.Properties.UserProperties { 267 keys = append(keys, key) 268 } 269 sort.Strings(keys) 270 for _, key := range keys { 271 fmt.Fprintf(tw, " %s\t%s\n", key, r.Properties.UserProperties[key]) 272 } 273 tw.Flush() 274 }() 275 } 276 } 277 278 func (s *sstableT) runScan(cmd *cobra.Command, args []string) { 279 for _, arg := range args { 280 func() { 281 f, err := vfs.Default.Open(arg) 282 if err != nil { 283 fmt.Fprintf(stderr, "%s\n", err) 284 return 285 } 286 287 fmt.Fprintf(stdout, "%s\n", arg) 288 289 r, err := s.newReader(f) 290 defer r.Close() 291 292 if err != nil { 293 fmt.Fprintf(stdout, "%s\n", err) 294 return 295 } 296 297 iter := r.NewIter(nil, s.end) 298 var lastKey base.InternalKey 299 for key, value := iter.SeekGE(s.start); key != nil; key, value = iter.Next() { 300 formatKeyValue(stdout, s.fmtKey, s.fmtValue, key, value) 301 if base.InternalCompare(r.Compare, lastKey, *key) >= 0 { 302 fmt.Fprintf(stdout, " WARNING: OUT OF ORDER KEYS!\n") 303 } 304 lastKey.Trailer = key.Trailer 305 lastKey.UserKey = append(lastKey.UserKey[:0], key.UserKey...) 306 } 307 if err := iter.Close(); err != nil { 308 fmt.Fprintf(stdout, "%s\n", err) 309 } 310 }() 311 } 312 }