github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/cmd/noms/noms_show.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package main 23 24 import ( 25 "bytes" 26 "context" 27 "errors" 28 "fmt" 29 "io" 30 "os" 31 "time" 32 33 flag "github.com/juju/gnuflag" 34 35 "github.com/dolthub/dolt/go/gen/fb/serial" 36 "github.com/dolthub/dolt/go/store/cmd/noms/util" 37 "github.com/dolthub/dolt/go/store/config" 38 "github.com/dolthub/dolt/go/store/hash" 39 "github.com/dolthub/dolt/go/store/prolly/shim" 40 "github.com/dolthub/dolt/go/store/prolly/tree" 41 "github.com/dolthub/dolt/go/store/types" 42 "github.com/dolthub/dolt/go/store/util/datetime" 43 "github.com/dolthub/dolt/go/store/util/outputpager" 44 "github.com/dolthub/dolt/go/store/util/verbose" 45 ) 46 47 var nomsShow = &util.Command{ 48 Run: runShow, 49 UsageLine: "show [flags] <object>", 50 Short: "Shows a serialization of a Noms object", 51 Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.", 52 Flags: setupShowFlags, 53 Nargs: 1, 54 } 55 56 var ( 57 showRaw = false 58 showStats = false 59 showPages = false 60 tzName string 61 ) 62 63 func setupShowFlags() *flag.FlagSet { 64 showFlagSet := flag.NewFlagSet("show", flag.ExitOnError) 65 outputpager.RegisterOutputpagerFlags(showFlagSet) 66 verbose.RegisterVerboseFlags(showFlagSet) 67 showFlagSet.BoolVar(&showPages, "page", false, "If true output is shown in an output pager") 68 showFlagSet.BoolVar(&showRaw, "raw", false, "If true, dumps the raw binary version of the data") 69 showFlagSet.BoolVar(&showStats, "stats", false, "If true, reports statistics related to the value") 70 showFlagSet.StringVar(&tzName, "tz", "local", "display formatted date comments in specified timezone, must be: local or utc") 71 return showFlagSet 72 } 73 74 func runShow(ctx context.Context, args []string) int { 75 cfg := config.NewResolver() 76 77 var value types.Value 78 database, vrw, value, err := cfg.GetPath(ctx, args[0]) 79 80 if err != nil { 81 util.CheckErrorNoUsage(err) 82 } else { 83 } 84 85 defer database.Close() 86 87 if value == nil { 88 fmt.Fprintf(os.Stderr, "Object not found: %s\n", args[0]) 89 return 0 90 } 91 92 if showRaw && showStats { 93 fmt.Fprintln(os.Stderr, "--raw and --stats are mutually exclusive") 94 return 0 95 } 96 97 if showRaw { 98 ch, err := types.EncodeValue(value.(types.Value), vrw.Format()) 99 util.CheckError(err) 100 buf := bytes.NewBuffer(ch.Data()) 101 _, err = io.Copy(os.Stdout, buf) 102 util.CheckError(err) 103 return 0 104 } 105 106 if showStats { 107 types.WriteValueStats(ctx, os.Stdout, value.(types.Value), vrw) 108 return 0 109 } 110 111 tz, _ := locationFromTimezoneArg(tzName, nil) 112 datetime.RegisterHRSCommenter(tz) 113 114 if showPages { 115 pgr := outputpager.Start() 116 defer pgr.Stop() 117 118 outputEncodedValue(ctx, pgr.Writer, value) 119 fmt.Fprintln(pgr.Writer) 120 } else { 121 outputType(value) 122 outputEncodedValue(ctx, os.Stdout, value) 123 } 124 125 return 0 126 } 127 128 func outputType(value types.Value) { 129 typeString := typeString(value) 130 fmt.Fprint(os.Stdout, typeString, " - ") 131 } 132 133 func typeString(value types.Value) string { 134 var typeString string 135 switch value := value.(type) { 136 case types.SerialMessage: 137 switch serial.GetFileID(value) { 138 case serial.StoreRootFileID: 139 typeString = "StoreRoot" 140 case serial.StashListFileID: 141 typeString = "StashList" 142 case serial.StashFileID: 143 typeString = "Stash" 144 case serial.TagFileID: 145 typeString = "Tag" 146 case serial.WorkingSetFileID: 147 typeString = "WorkingSet" 148 case serial.CommitFileID: 149 typeString = "Commit" 150 case serial.RootValueFileID: 151 typeString = "RootValue" 152 case serial.DoltgresRootValueFileID: 153 typeString = "DoltgresRootValue" 154 case serial.TableFileID: 155 typeString = "Table" 156 case serial.ProllyTreeNodeFileID: 157 typeString = "ProllyTreeNode" 158 case serial.AddressMapFileID: 159 typeString = "AddressMap" 160 case serial.CommitClosureFileID: 161 typeString = "CommitClosure" 162 case serial.TableSchemaFileID: 163 typeString = "TableSchema" 164 default: 165 t, err := types.TypeOf(value) 166 util.CheckErrorNoUsage(err) 167 typeString = t.HumanReadableString() 168 } 169 default: 170 t, err := types.TypeOf(value) 171 util.CheckErrorNoUsage(err) 172 typeString = t.HumanReadableString() 173 } 174 return typeString 175 } 176 177 func outputEncodedValue(ctx context.Context, w io.Writer, value types.Value) error { 178 switch value := value.(type) { 179 // Some types of serial message need to be output here because of dependency cycles between types / tree package 180 case types.SerialMessage: 181 switch serial.GetFileID(value) { 182 case serial.TableFileID: 183 msg, err := serial.TryGetRootAsTable(value, serial.MessagePrefixSz) 184 if err != nil { 185 return err 186 } 187 188 fmt.Fprintf(w, " {\n") 189 fmt.Fprintf(w, "\tSchema: #%s\n", hash.New(msg.SchemaBytes()).String()) 190 fmt.Fprintf(w, "\tViolations: #%s\n", hash.New(msg.ViolationsBytes()).String()) 191 fmt.Fprintf(w, "\tArtifacts: #%s\n", hash.New(msg.ArtifactsBytes()).String()) 192 // TODO: merge conflicts, not stable yet 193 194 fmt.Fprintf(w, "\tAutoinc: %d\n", msg.AutoIncrementValue()) 195 196 // clustered index 197 node, err := tree.NodeFromBytes(msg.PrimaryIndexBytes()) 198 if err != nil { 199 return err 200 } 201 c, err := node.TreeCount() 202 if err != nil { 203 return err 204 } 205 fmt.Fprintf(w, "\tPrimary Index (rows %d, depth %d) #%s {", 206 c, node.Level()+1, node.HashOf().String()) 207 tree.OutputProllyNodeBytes(w, node) 208 fmt.Fprintf(w, "\t}\n") 209 210 // secondary indexes 211 node, err = tree.NodeFromBytes(msg.SecondaryIndexesBytes()) 212 if err != nil { 213 return err 214 } 215 c, err = node.TreeCount() 216 if err != nil { 217 return err 218 } 219 fmt.Fprintf(w, "\tSecondary Indexes (indexes %d, depth %d) %s {", 220 c, node.Level()+1, node.HashOf().String()[:8]) 221 err = tree.OutputAddressMapNode(w, node) 222 if err != nil { 223 return err 224 } 225 fmt.Fprintf(w, "\t}\n") 226 fmt.Fprintf(w, "}") 227 228 return nil 229 case serial.StoreRootFileID: 230 msg, err := serial.TryGetRootAsStoreRoot(value, serial.MessagePrefixSz) 231 if err != nil { 232 return err 233 } 234 ambytes := msg.AddressMapBytes() 235 node, err := tree.NodeFromBytes(ambytes) 236 if err != nil { 237 return err 238 } 239 return tree.OutputAddressMapNode(w, node) 240 case serial.ProllyTreeNodeFileID: 241 fallthrough 242 case serial.AddressMapFileID: 243 node, err := shim.NodeFromValue(value) 244 if err != nil { 245 return err 246 } 247 return tree.OutputProllyNodeBytes(w, node) 248 default: 249 return types.WriteEncodedValue(ctx, w, value) 250 } 251 default: 252 return types.WriteEncodedValue(ctx, w, value) 253 } 254 } 255 256 func locationFromTimezoneArg(tz string, defaultTZ *time.Location) (*time.Location, error) { 257 switch tz { 258 case "local": 259 return time.Local, nil 260 case "utc": 261 return time.UTC, nil 262 case "": 263 return defaultTZ, nil 264 default: 265 return nil, errors.New("value must be: local or utc") 266 } 267 }