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  }