github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/keys/printer.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package keys
    12  
    13  import (
    14  	"bytes"
    15  	"fmt"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    21  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    22  	"github.com/cockroachdb/errors"
    23  )
    24  
    25  // PrettyPrintTimeseriesKey is a hook for pretty printing a timeseries key. The
    26  // timeseries key prefix will already have been stripped off.
    27  var PrettyPrintTimeseriesKey func(key roachpb.Key) string
    28  
    29  // DictEntry contains info on pretty-printing and pretty-scanning keys in a
    30  // region of the key space.
    31  type DictEntry struct {
    32  	Name   string
    33  	prefix roachpb.Key
    34  	// print the key's pretty value, key has been removed prefix data
    35  	ppFunc func(valDirs []encoding.Direction, key roachpb.Key) string
    36  	// PSFunc parses the relevant prefix of the input into a roachpb.Key,
    37  	// returning the remainder and the key corresponding to the consumed prefix of
    38  	// 'input'. Allowed to panic on errors.
    39  	PSFunc KeyParserFunc
    40  }
    41  
    42  // KeyParserFunc is a function able to reverse pretty-printed keys.
    43  type KeyParserFunc func(input string) (string, roachpb.Key)
    44  
    45  func parseUnsupported(_ string) (string, roachpb.Key) {
    46  	panic(&ErrUglifyUnsupported{})
    47  }
    48  
    49  // KeyComprehensionTable contains information about how to decode pretty-printed
    50  // keys, split by key spans.
    51  type KeyComprehensionTable []struct {
    52  	Name    string
    53  	start   roachpb.Key
    54  	end     roachpb.Key
    55  	Entries []DictEntry
    56  }
    57  
    58  var (
    59  	// ConstKeyDict translates some pretty-printed keys.
    60  	ConstKeyDict = []struct {
    61  		Name  string
    62  		Value roachpb.Key
    63  	}{
    64  		{"/Max", MaxKey},
    65  		{"/Min", MinKey},
    66  		{"/Meta1/Max", Meta1KeyMax},
    67  		{"/Meta2/Max", Meta2KeyMax},
    68  	}
    69  
    70  	// KeyDict drives the pretty-printing and pretty-scanning of the key space.
    71  	KeyDict = KeyComprehensionTable{
    72  		{Name: "/Local", start: localPrefix, end: LocalMax, Entries: []DictEntry{
    73  			{Name: "/Store", prefix: roachpb.Key(localStorePrefix),
    74  				ppFunc: localStoreKeyPrint, PSFunc: localStoreKeyParse},
    75  			{Name: "/RangeID", prefix: roachpb.Key(LocalRangeIDPrefix),
    76  				ppFunc: localRangeIDKeyPrint, PSFunc: localRangeIDKeyParse},
    77  			{Name: "/Range", prefix: LocalRangePrefix, ppFunc: localRangeKeyPrint,
    78  				PSFunc: parseUnsupported},
    79  		}},
    80  		{Name: "/Meta1", start: Meta1Prefix, end: Meta1KeyMax, Entries: []DictEntry{
    81  			{Name: "", prefix: Meta1Prefix, ppFunc: print,
    82  				PSFunc: func(input string) (string, roachpb.Key) {
    83  					input = mustShiftSlash(input)
    84  					unq, err := strconv.Unquote(input)
    85  					if err != nil {
    86  						panic(err)
    87  					}
    88  					if len(unq) == 0 {
    89  						return "", Meta1Prefix
    90  					}
    91  					return "", RangeMetaKey(RangeMetaKey(MustAddr(
    92  						roachpb.Key(unq)))).AsRawKey()
    93  				},
    94  			}},
    95  		},
    96  		{Name: "/Meta2", start: Meta2Prefix, end: Meta2KeyMax, Entries: []DictEntry{
    97  			{Name: "", prefix: Meta2Prefix, ppFunc: print,
    98  				PSFunc: func(input string) (string, roachpb.Key) {
    99  					input = mustShiftSlash(input)
   100  					unq, err := strconv.Unquote(input)
   101  					if err != nil {
   102  						panic(&ErrUglifyUnsupported{err})
   103  					}
   104  					if len(unq) == 0 {
   105  						return "", Meta2Prefix
   106  					}
   107  					return "", RangeMetaKey(MustAddr(roachpb.Key(unq))).AsRawKey()
   108  				},
   109  			}},
   110  		},
   111  		{Name: "/System", start: SystemPrefix, end: SystemMax, Entries: []DictEntry{
   112  			{Name: "/NodeLiveness", prefix: NodeLivenessPrefix,
   113  				ppFunc: decodeKeyPrint,
   114  				PSFunc: parseUnsupported,
   115  			},
   116  			{Name: "/NodeLivenessMax", prefix: NodeLivenessKeyMax,
   117  				ppFunc: decodeKeyPrint,
   118  				PSFunc: parseUnsupported,
   119  			},
   120  			{Name: "/StatusNode", prefix: StatusNodePrefix,
   121  				ppFunc: decodeKeyPrint,
   122  				PSFunc: parseUnsupported,
   123  			},
   124  			{Name: "/tsd", prefix: TimeseriesPrefix,
   125  				ppFunc: timeseriesKeyPrint,
   126  				PSFunc: parseUnsupported,
   127  			},
   128  		}},
   129  		{Name: "/NamespaceTable", start: NamespaceTableMin, end: NamespaceTableMax, Entries: []DictEntry{
   130  			{Name: "", prefix: nil, ppFunc: decodeKeyPrint, PSFunc: parseUnsupported},
   131  		}},
   132  		{Name: "/Table", start: TableDataMin, end: TableDataMax, Entries: []DictEntry{
   133  			{Name: "", prefix: nil, ppFunc: decodeKeyPrint, PSFunc: tableKeyParse},
   134  		}},
   135  		{Name: "/Tenant", start: TenantTableDataMin, end: TenantTableDataMax, Entries: []DictEntry{
   136  			{Name: "", prefix: nil, ppFunc: tenantKeyPrint, PSFunc: tenantKeyParse},
   137  		}},
   138  	}
   139  
   140  	// keyofKeyDict means the key of suffix which is itself a key,
   141  	// should recursively pretty print it, see issue #3228
   142  	keyOfKeyDict = []struct {
   143  		name   string
   144  		prefix []byte
   145  	}{
   146  		{name: "/Meta2", prefix: Meta2Prefix},
   147  		{name: "/Meta1", prefix: Meta1Prefix},
   148  	}
   149  
   150  	rangeIDSuffixDict = []struct {
   151  		name   string
   152  		suffix []byte
   153  		ppFunc func(key roachpb.Key) string
   154  		psFunc func(rangeID roachpb.RangeID, input string) (string, roachpb.Key)
   155  	}{
   156  		{name: "AbortSpan", suffix: LocalAbortSpanSuffix, ppFunc: abortSpanKeyPrint, psFunc: abortSpanKeyParse},
   157  		{name: "RangeTombstone", suffix: LocalRangeTombstoneSuffix},
   158  		{name: "RaftHardState", suffix: LocalRaftHardStateSuffix},
   159  		{name: "RangeAppliedState", suffix: LocalRangeAppliedStateSuffix},
   160  		{name: "RaftAppliedIndex", suffix: LocalRaftAppliedIndexLegacySuffix},
   161  		{name: "LeaseAppliedIndex", suffix: LocalLeaseAppliedIndexLegacySuffix},
   162  		{name: "RaftLog", suffix: LocalRaftLogSuffix,
   163  			ppFunc: raftLogKeyPrint,
   164  			psFunc: raftLogKeyParse,
   165  		},
   166  		{name: "RaftTruncatedState", suffix: LocalRaftTruncatedStateLegacySuffix},
   167  		{name: "RangeLastReplicaGCTimestamp", suffix: LocalRangeLastReplicaGCTimestampSuffix},
   168  		{name: "RangeLease", suffix: LocalRangeLeaseSuffix},
   169  		{name: "RangeStats", suffix: LocalRangeStatsLegacySuffix},
   170  		{name: "RangeLastGC", suffix: LocalRangeLastGCSuffix},
   171  	}
   172  
   173  	rangeSuffixDict = []struct {
   174  		name   string
   175  		suffix []byte
   176  		atEnd  bool
   177  	}{
   178  		{name: "RangeDescriptor", suffix: LocalRangeDescriptorSuffix, atEnd: true},
   179  		{name: "Transaction", suffix: LocalTransactionSuffix, atEnd: false},
   180  		{name: "QueueLastProcessed", suffix: LocalQueueLastProcessedSuffix, atEnd: false},
   181  	}
   182  )
   183  
   184  var constSubKeyDict = []struct {
   185  	name string
   186  	key  roachpb.RKey
   187  }{
   188  	{"/storeIdent", localStoreIdentSuffix},
   189  	{"/gossipBootstrap", localStoreGossipSuffix},
   190  	{"/clusterVersion", localStoreClusterVersionSuffix},
   191  	{"/suggestedCompaction", localStoreSuggestedCompactionSuffix},
   192  }
   193  
   194  func suggestedCompactionKeyPrint(key roachpb.Key) string {
   195  	start, end, err := DecodeStoreSuggestedCompactionKey(key)
   196  	if err != nil {
   197  		return fmt.Sprintf("<invalid: %s>", err)
   198  	}
   199  	return fmt.Sprintf("{%s-%s}", start, end)
   200  }
   201  
   202  func localStoreKeyPrint(_ []encoding.Direction, key roachpb.Key) string {
   203  	for _, v := range constSubKeyDict {
   204  		if bytes.HasPrefix(key, v.key) {
   205  			if v.key.Equal(localStoreSuggestedCompactionSuffix) {
   206  				return v.name + "/" + suggestedCompactionKeyPrint(
   207  					append(roachpb.Key(nil), append(localStorePrefix, key...)...),
   208  				)
   209  			}
   210  			return v.name
   211  		}
   212  	}
   213  
   214  	return fmt.Sprintf("%q", []byte(key))
   215  }
   216  
   217  func localStoreKeyParse(input string) (remainder string, output roachpb.Key) {
   218  	for _, s := range constSubKeyDict {
   219  		if strings.HasPrefix(input, s.name) {
   220  			if s.key.Equal(localStoreSuggestedCompactionSuffix) {
   221  				panic(&ErrUglifyUnsupported{errors.New("cannot parse suggested compaction key")})
   222  			}
   223  			output = MakeStoreKey(s.key, nil)
   224  			return
   225  		}
   226  	}
   227  	input = mustShiftSlash(input)
   228  	slashPos := strings.IndexByte(input, '/')
   229  	if slashPos < 0 {
   230  		slashPos = len(input)
   231  	}
   232  	remainder = input[slashPos:] // `/something/else` -> `/else`
   233  	output = roachpb.Key(input[:slashPos])
   234  	return
   235  }
   236  
   237  const strTable = "/Table/"
   238  const strSystemConfigSpan = "SystemConfigSpan"
   239  const strSystemConfigSpanStart = "Start"
   240  
   241  func tenantKeyParse(input string) (remainder string, output roachpb.Key) {
   242  	input = mustShiftSlash(input)
   243  	slashPos := strings.Index(input, "/")
   244  	if slashPos < 0 {
   245  		slashPos = len(input)
   246  	}
   247  	remainder = input[slashPos:] // `/something/else` -> `/else`
   248  	tenantIDStr := input[:slashPos]
   249  	tenantID, err := strconv.ParseUint(tenantIDStr, 10, 64)
   250  	if err != nil {
   251  		panic(&ErrUglifyUnsupported{err})
   252  	}
   253  	output = MakeTenantPrefix(roachpb.MakeTenantID(tenantID))
   254  	if strings.HasPrefix(remainder, strTable) {
   255  		var indexKey roachpb.Key
   256  		remainder = remainder[len(strTable)-1:]
   257  		remainder, indexKey = tableKeyParse(remainder)
   258  		output = append(output, indexKey...)
   259  	}
   260  	return remainder, output
   261  }
   262  
   263  func tableKeyParse(input string) (remainder string, output roachpb.Key) {
   264  	input = mustShiftSlash(input)
   265  	slashPos := strings.Index(input, "/")
   266  	if slashPos < 0 {
   267  		slashPos = len(input)
   268  	}
   269  	remainder = input[slashPos:] // `/something/else` -> `/else`
   270  	tableIDStr := input[:slashPos]
   271  	if tableIDStr == strSystemConfigSpan {
   272  		if remainder[1:] == strSystemConfigSpanStart {
   273  			remainder = ""
   274  		}
   275  		output = SystemConfigSpan.Key
   276  		return
   277  	}
   278  	tableID, err := strconv.ParseUint(tableIDStr, 10, 32)
   279  	if err != nil {
   280  		panic(&ErrUglifyUnsupported{err})
   281  	}
   282  	output = encoding.EncodeUvarintAscending(nil /* key */, tableID)
   283  	if remainder != "" {
   284  		var indexKey roachpb.Key
   285  		remainder, indexKey = tableIndexParse(remainder)
   286  		output = append(output, indexKey...)
   287  	}
   288  	return remainder, output
   289  }
   290  
   291  // tableIndexParse parses an index id out of the input and returns the remainder.
   292  // The input is expected to be of the form "/<index id>[/...]".
   293  func tableIndexParse(input string) (string, roachpb.Key) {
   294  	input = mustShiftSlash(input)
   295  	slashPos := strings.Index(input, "/")
   296  	if slashPos < 0 {
   297  		// We accept simply "/<id>"; if there's no further slashes, the whole string
   298  		// has to be the index id.
   299  		slashPos = len(input)
   300  	}
   301  	remainder := input[slashPos:] // `/something/else` -> `/else`
   302  	indexIDStr := input[:slashPos]
   303  	indexID, err := strconv.ParseUint(indexIDStr, 10, 32)
   304  	if err != nil {
   305  		panic(&ErrUglifyUnsupported{err})
   306  	}
   307  	output := encoding.EncodeUvarintAscending(nil /* key */, indexID)
   308  	return remainder, output
   309  }
   310  
   311  const strLogIndex = "/logIndex:"
   312  
   313  func raftLogKeyParse(rangeID roachpb.RangeID, input string) (string, roachpb.Key) {
   314  	if !strings.HasPrefix(input, strLogIndex) {
   315  		panic("expected log index")
   316  	}
   317  	input = input[len(strLogIndex):]
   318  	index, err := strconv.ParseUint(input, 10, 64)
   319  	if err != nil {
   320  		panic(err)
   321  	}
   322  	return "", RaftLogKey(rangeID, index)
   323  }
   324  
   325  func raftLogKeyPrint(key roachpb.Key) string {
   326  	var logIndex uint64
   327  	var err error
   328  	key, logIndex, err = encoding.DecodeUint64Ascending(key)
   329  	if err != nil {
   330  		return fmt.Sprintf("/err<%v:%q>", err, []byte(key))
   331  	}
   332  
   333  	return fmt.Sprintf("%s%d", strLogIndex, logIndex)
   334  }
   335  
   336  func mustShiftSlash(in string) string {
   337  	slash, out := mustShift(in)
   338  	if slash != "/" {
   339  		panic("expected /: " + in)
   340  	}
   341  	return out
   342  }
   343  
   344  func mustShift(in string) (first, remainder string) {
   345  	if len(in) == 0 {
   346  		panic("premature end of string")
   347  	}
   348  	return in[:1], in[1:]
   349  }
   350  
   351  func localRangeIDKeyParse(input string) (remainder string, key roachpb.Key) {
   352  	var rangeID int64
   353  	var err error
   354  	input = mustShiftSlash(input)
   355  	if endPos := strings.IndexByte(input, '/'); endPos > 0 {
   356  		rangeID, err = strconv.ParseInt(input[:endPos], 10, 64)
   357  		if err != nil {
   358  			panic(err)
   359  		}
   360  		input = input[endPos:]
   361  	} else {
   362  		panic(errors.Errorf("illegal RangeID: %q", input))
   363  	}
   364  	input = mustShiftSlash(input)
   365  	var infix string
   366  	infix, input = mustShift(input)
   367  	var replicated bool
   368  	switch {
   369  	case bytes.Equal(localRangeIDUnreplicatedInfix, []byte(infix)):
   370  	case bytes.Equal(LocalRangeIDReplicatedInfix, []byte(infix)):
   371  		replicated = true
   372  	default:
   373  		panic(errors.Errorf("invalid infix: %q", infix))
   374  	}
   375  
   376  	input = mustShiftSlash(input)
   377  	// Get the suffix.
   378  	var suffix roachpb.RKey
   379  	for _, s := range rangeIDSuffixDict {
   380  		if strings.HasPrefix(input, s.name) {
   381  			input = input[len(s.name):]
   382  			if s.psFunc != nil {
   383  				remainder, key = s.psFunc(roachpb.RangeID(rangeID), input)
   384  				return
   385  			}
   386  			suffix = roachpb.RKey(s.suffix)
   387  			break
   388  		}
   389  	}
   390  	maker := makeRangeIDUnreplicatedKey
   391  	if replicated {
   392  		maker = makeRangeIDReplicatedKey
   393  	}
   394  	if suffix != nil {
   395  		if input != "" {
   396  			panic(&ErrUglifyUnsupported{errors.New("nontrivial detail")})
   397  		}
   398  		var detail roachpb.RKey
   399  		// TODO(tschottdorf): can't do this, init cycle:
   400  		// detail, err := UglyPrint(input)
   401  		// if err != nil {
   402  		// 	return "", nil, err
   403  		// }
   404  		remainder = ""
   405  		key = maker(roachpb.RangeID(rangeID), suffix, detail)
   406  		return
   407  	}
   408  	panic(&ErrUglifyUnsupported{errors.New("unhandled general range key")})
   409  }
   410  
   411  func localRangeIDKeyPrint(valDirs []encoding.Direction, key roachpb.Key) string {
   412  	var buf bytes.Buffer
   413  	if encoding.PeekType(key) != encoding.Int {
   414  		return fmt.Sprintf("/err<%q>", []byte(key))
   415  	}
   416  
   417  	// Get the rangeID.
   418  	key, i, err := encoding.DecodeVarintAscending(key)
   419  	if err != nil {
   420  		return fmt.Sprintf("/err<%v:%q>", err, []byte(key))
   421  	}
   422  
   423  	fmt.Fprintf(&buf, "/%d", i)
   424  
   425  	// Print and remove the rangeID infix specifier.
   426  	if len(key) != 0 {
   427  		fmt.Fprintf(&buf, "/%s", string(key[0]))
   428  		key = key[1:]
   429  	}
   430  
   431  	// Get the suffix.
   432  	hasSuffix := false
   433  	for _, s := range rangeIDSuffixDict {
   434  		if bytes.HasPrefix(key, s.suffix) {
   435  			fmt.Fprintf(&buf, "/%s", s.name)
   436  			key = key[len(s.suffix):]
   437  			if s.ppFunc != nil && len(key) != 0 {
   438  				fmt.Fprintf(&buf, "%s", s.ppFunc(key))
   439  				return buf.String()
   440  			}
   441  			hasSuffix = true
   442  			break
   443  		}
   444  	}
   445  
   446  	// Get the encode values.
   447  	if hasSuffix {
   448  		fmt.Fprintf(&buf, "%s", decodeKeyPrint(valDirs, key))
   449  	} else {
   450  		fmt.Fprintf(&buf, "%q", []byte(key))
   451  	}
   452  
   453  	return buf.String()
   454  }
   455  
   456  func localRangeKeyPrint(valDirs []encoding.Direction, key roachpb.Key) string {
   457  	var buf bytes.Buffer
   458  
   459  	for _, s := range rangeSuffixDict {
   460  		if s.atEnd {
   461  			if bytes.HasSuffix(key, s.suffix) {
   462  				key = key[:len(key)-len(s.suffix)]
   463  				_, decodedKey, err := encoding.DecodeBytesAscending([]byte(key), nil)
   464  				if err != nil {
   465  					fmt.Fprintf(&buf, "%s/%s", decodeKeyPrint(valDirs, key), s.name)
   466  				} else {
   467  					fmt.Fprintf(&buf, "%s/%s", roachpb.Key(decodedKey), s.name)
   468  				}
   469  				return buf.String()
   470  			}
   471  		} else {
   472  			begin := bytes.Index(key, s.suffix)
   473  			if begin > 0 {
   474  				addrKey := key[:begin]
   475  				_, decodedAddrKey, err := encoding.DecodeBytesAscending([]byte(addrKey), nil)
   476  				if err != nil {
   477  					fmt.Fprintf(&buf, "%s/%s", decodeKeyPrint(valDirs, addrKey), s.name)
   478  				} else {
   479  					fmt.Fprintf(&buf, "%s/%s", roachpb.Key(decodedAddrKey), s.name)
   480  				}
   481  				if bytes.Equal(s.suffix, LocalTransactionSuffix) {
   482  					txnID, err := uuid.FromBytes(key[(begin + len(s.suffix)):])
   483  					if err != nil {
   484  						return fmt.Sprintf("/%q/err:%v", key, err)
   485  					}
   486  					fmt.Fprintf(&buf, "/%q", txnID)
   487  				} else {
   488  					id := key[(begin + len(s.suffix)):]
   489  					fmt.Fprintf(&buf, "/%q", []byte(id))
   490  				}
   491  				return buf.String()
   492  			}
   493  		}
   494  	}
   495  
   496  	_, decodedKey, err := encoding.DecodeBytesAscending([]byte(key), nil)
   497  	if err != nil {
   498  		fmt.Fprintf(&buf, "%s", decodeKeyPrint(valDirs, key))
   499  	} else {
   500  		fmt.Fprintf(&buf, "%s", roachpb.Key(decodedKey))
   501  	}
   502  
   503  	return buf.String()
   504  }
   505  
   506  // ErrUglifyUnsupported is returned when UglyPrint doesn't know how to process a
   507  // key.
   508  type ErrUglifyUnsupported struct {
   509  	Wrapped error
   510  }
   511  
   512  func (euu *ErrUglifyUnsupported) Error() string {
   513  	return fmt.Sprintf("unsupported pretty key: %v", euu.Wrapped)
   514  }
   515  
   516  func abortSpanKeyParse(rangeID roachpb.RangeID, input string) (string, roachpb.Key) {
   517  	var err error
   518  	input = mustShiftSlash(input)
   519  	_, input = mustShift(input[:len(input)-1])
   520  	if len(input) != len(uuid.UUID{}.String()) {
   521  		panic(&ErrUglifyUnsupported{errors.New("txn id not available")})
   522  	}
   523  	id, err := uuid.FromString(input)
   524  	if err != nil {
   525  		panic(&ErrUglifyUnsupported{err})
   526  	}
   527  	return "", AbortSpanKey(rangeID, id)
   528  }
   529  
   530  func abortSpanKeyPrint(key roachpb.Key) string {
   531  	_, id, err := encoding.DecodeBytesAscending([]byte(key), nil)
   532  	if err != nil {
   533  		return fmt.Sprintf("/%q/err:%v", key, err)
   534  	}
   535  
   536  	txnID, err := uuid.FromBytes(id)
   537  	if err != nil {
   538  		return fmt.Sprintf("/%q/err:%v", key, err)
   539  	}
   540  
   541  	return fmt.Sprintf("/%q", txnID)
   542  }
   543  
   544  func print(_ []encoding.Direction, key roachpb.Key) string {
   545  	return fmt.Sprintf("/%q", []byte(key))
   546  }
   547  
   548  func decodeKeyPrint(valDirs []encoding.Direction, key roachpb.Key) string {
   549  	if key.Equal(SystemConfigSpan.Key) {
   550  		return "/SystemConfigSpan/Start"
   551  	}
   552  	return encoding.PrettyPrintValue(valDirs, key, "/")
   553  }
   554  
   555  func timeseriesKeyPrint(_ []encoding.Direction, key roachpb.Key) string {
   556  	return PrettyPrintTimeseriesKey(key)
   557  }
   558  
   559  func tenantKeyPrint(valDirs []encoding.Direction, key roachpb.Key) string {
   560  	key, tID, err := DecodeTenantPrefix(key)
   561  	if err != nil {
   562  		return fmt.Sprintf("/err:%v", err)
   563  	}
   564  	if len(key) == 0 {
   565  		return fmt.Sprintf("/%s", tID)
   566  	}
   567  	return fmt.Sprintf("/%s%s", tID, key.StringWithDirs(valDirs, 0))
   568  }
   569  
   570  // prettyPrintInternal parse key with prefix in KeyDict.
   571  // For table keys, valDirs correspond to the encoding direction of each encoded
   572  // value in key.
   573  // If valDirs is unspecified, the default encoding direction for each value
   574  // type is used (see encoding.go:prettyPrintFirstValue).
   575  // If the key doesn't match any prefix in KeyDict, return its byte value with
   576  // quotation and false, or else return its human readable value and true.
   577  func prettyPrintInternal(valDirs []encoding.Direction, key roachpb.Key, quoteRawKeys bool) string {
   578  	for _, k := range ConstKeyDict {
   579  		if key.Equal(k.Value) {
   580  			return k.Name
   581  		}
   582  	}
   583  
   584  	helper := func(key roachpb.Key) (string, bool) {
   585  		var b strings.Builder
   586  		for _, k := range KeyDict {
   587  			if key.Compare(k.start) >= 0 && (k.end == nil || key.Compare(k.end) <= 0) {
   588  				b.WriteString(k.Name)
   589  				if k.end != nil && k.end.Compare(key) == 0 {
   590  					b.WriteString("/Max")
   591  					return b.String(), true
   592  				}
   593  
   594  				hasPrefix := false
   595  				for _, e := range k.Entries {
   596  					if bytes.HasPrefix(key, e.prefix) {
   597  						hasPrefix = true
   598  						key = key[len(e.prefix):]
   599  						b.WriteString(e.Name)
   600  						b.WriteString(e.ppFunc(valDirs, key))
   601  						break
   602  					}
   603  				}
   604  				if !hasPrefix {
   605  					key = key[len(k.start):]
   606  					if quoteRawKeys {
   607  						b.WriteByte('/')
   608  						b.WriteByte('"')
   609  					}
   610  					b.Write([]byte(key))
   611  					if quoteRawKeys {
   612  						b.WriteByte('"')
   613  					}
   614  				}
   615  
   616  				return b.String(), true
   617  			}
   618  		}
   619  
   620  		if quoteRawKeys {
   621  			return fmt.Sprintf("%q", []byte(key)), false
   622  		}
   623  		return fmt.Sprintf("%s", []byte(key)), false
   624  	}
   625  
   626  	for _, k := range keyOfKeyDict {
   627  		if bytes.HasPrefix(key, k.prefix) {
   628  			key = key[len(k.prefix):]
   629  			str, formatted := helper(key)
   630  			if formatted {
   631  				return k.name + str
   632  			}
   633  			return k.name + "/" + str
   634  		}
   635  	}
   636  	str, _ := helper(key)
   637  	return str
   638  }
   639  
   640  // PrettyPrint prints the key in a human readable format, see TestPrettyPrint.
   641  // The output does not indicate whether a key is part of the replicated or un-
   642  // replicated keyspace.
   643  //
   644  // valDirs correspond to the encoding direction of each encoded value in key.
   645  // For example, table keys could have column values encoded in ascending or
   646  // descending directions.
   647  // If valDirs is unspecified, the default encoding direction for each value
   648  // type is used (see encoding.go:prettyPrintFirstValue).
   649  //
   650  // See keysutil.UglyPrint() for an inverse.
   651  func PrettyPrint(valDirs []encoding.Direction, key roachpb.Key) string {
   652  	return prettyPrintInternal(valDirs, key, true /* quoteRawKeys */)
   653  }
   654  
   655  func init() {
   656  	roachpb.PrettyPrintKey = PrettyPrint
   657  	roachpb.PrettyPrintRange = PrettyPrintRange
   658  }
   659  
   660  // MassagePrettyPrintedSpanForTest does some transformations on pretty-printed spans and keys:
   661  // - if dirs is not nil, replace all ints with their ones' complement for
   662  // descendingly-encoded columns.
   663  // - strips line numbers from error messages.
   664  func MassagePrettyPrintedSpanForTest(span string, dirs []encoding.Direction) string {
   665  	var r string
   666  	colIdx := -1
   667  	for i := 0; i < len(span); i++ {
   668  		if dirs != nil {
   669  			var d int
   670  			if _, err := fmt.Sscanf(span[i:], "%d", &d); err != nil {
   671  				// We've managed to consume an int.
   672  				dir := dirs[colIdx]
   673  				i += len(strconv.Itoa(d)) - 1
   674  				x := d
   675  				if dir == encoding.Descending {
   676  					x = ^x
   677  				}
   678  				r += strconv.Itoa(x)
   679  				continue
   680  			}
   681  		}
   682  		r += string(span[i])
   683  		switch span[i] {
   684  		case '/':
   685  			colIdx++
   686  		case '-', ' ':
   687  			// We're switching from the start constraints to the end constraints,
   688  			// or starting another span.
   689  			colIdx = -1
   690  		}
   691  	}
   692  	return r
   693  }
   694  
   695  // PrettyPrintRange pretty prints a compact representation of a key range. The
   696  // output is of the form:
   697  //    commonPrefix{remainingStart-remainingEnd}
   698  // If the end key is empty, the outut is of the form:
   699  //    start
   700  // It prints at most maxChars, truncating components as needed. See
   701  // TestPrettyPrintRange for some examples.
   702  func PrettyPrintRange(start, end roachpb.Key, maxChars int) string {
   703  	var b bytes.Buffer
   704  	if maxChars < 8 {
   705  		maxChars = 8
   706  	}
   707  	prettyStart := prettyPrintInternal(nil /* valDirs */, start, false /* quoteRawKeys */)
   708  	if len(end) == 0 {
   709  		if len(prettyStart) <= maxChars {
   710  			return prettyStart
   711  		}
   712  		b.WriteString(prettyStart[:maxChars-1])
   713  		b.WriteRune('…')
   714  		return b.String()
   715  	}
   716  	prettyEnd := prettyPrintInternal(nil /* valDirs */, end, false /* quoteRawKeys */)
   717  	i := 0
   718  	// Find the common prefix.
   719  	for ; i < len(prettyStart) && i < len(prettyEnd) && prettyStart[i] == prettyEnd[i]; i++ {
   720  	}
   721  	// If we don't have space for at least '{a…-b…}' after the prefix, only print
   722  	// the prefix (or part of it).
   723  	if i > maxChars-7 {
   724  		if i > maxChars-1 {
   725  			i = maxChars - 1
   726  		}
   727  		b.WriteString(prettyStart[:i])
   728  		b.WriteRune('…')
   729  		return b.String()
   730  	}
   731  	b.WriteString(prettyStart[:i])
   732  	remaining := (maxChars - i - 3) / 2
   733  
   734  	printTrunc := func(b *bytes.Buffer, what string, maxChars int) {
   735  		if len(what) <= maxChars {
   736  			b.WriteString(what)
   737  		} else {
   738  			b.WriteString(what[:maxChars-1])
   739  			b.WriteRune('…')
   740  		}
   741  	}
   742  
   743  	b.WriteByte('{')
   744  	printTrunc(&b, prettyStart[i:], remaining)
   745  	b.WriteByte('-')
   746  	printTrunc(&b, prettyEnd[i:], remaining)
   747  	b.WriteByte('}')
   748  
   749  	return b.String()
   750  }