github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/utils_test.go (about)

     1  // Copyright 2017 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 sqlbase
    12  
    13  import (
    14  	"fmt"
    15  	"strconv"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/apd"
    20  	"github.com/cockroachdb/cockroach/pkg/keys"
    21  	"github.com/cockroachdb/cockroach/pkg/kv"
    22  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    23  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    24  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    25  )
    26  
    27  var tableNames = map[string]bool{
    28  	"parent1":     true,
    29  	"child1":      true,
    30  	"grandchild1": true,
    31  	"child2":      true,
    32  	"parent2":     true,
    33  }
    34  
    35  // This file contains test helper and utility functions for sqlbase.
    36  
    37  // EncodeTestKey takes the short format representation of a key and transforms
    38  // it into an actual roachpb.Key. Refer to ShortToLongKeyFmt for more info. on
    39  // the short format.
    40  // All tokens are interpreted as UVarint (ascending) unless they satisfy:
    41  //    - '#' - interleaved sentinel
    42  //    - 's' first byte - string/bytes (ascending)
    43  //    - 'd' first byte - decimal (ascending)
    44  //    - NULLASC, NULLDESC, NOTNULLASC, NOTNULLDESC
    45  //    - PrefixEnd
    46  func EncodeTestKey(tb testing.TB, kvDB *kv.DB, codec keys.SQLCodec, keyStr string) roachpb.Key {
    47  	key := codec.TenantPrefix()
    48  	tokens := strings.Split(keyStr, "/")
    49  	if tokens[0] != "" {
    50  		panic("missing '/' token at the beginning of long format")
    51  	}
    52  
    53  	// Omit the first empty string.
    54  	tokens = tokens[1:]
    55  
    56  	for _, tok := range tokens {
    57  		if tok == "PrefixEnd" {
    58  			key = key.PrefixEnd()
    59  			continue
    60  		}
    61  
    62  		// Encode the table ID if the token is a table name.
    63  		if tableNames[tok] {
    64  			desc := GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tok)
    65  			key = encoding.EncodeUvarintAscending(key, uint64(desc.ID))
    66  			continue
    67  		}
    68  
    69  		switch tok[0] {
    70  		case 's':
    71  			key = encoding.EncodeStringAscending(key, tok[1:])
    72  			continue
    73  		case 'd':
    74  			dec, cond, err := apd.NewFromString(tok[1:])
    75  			if err != nil {
    76  				tb.Fatal(err)
    77  			}
    78  			if cond.Any() {
    79  				tb.Fatalf("encountered condition %s when parsing decimal", cond.String())
    80  			}
    81  			key = encoding.EncodeDecimalAscending(key, dec)
    82  			continue
    83  		}
    84  
    85  		if tok == "NULLASC" {
    86  			key = encoding.EncodeNullAscending(key)
    87  			continue
    88  		}
    89  
    90  		if tok == "NOTNULLASC" {
    91  			key = encoding.EncodeNotNullAscending(key)
    92  			continue
    93  		}
    94  
    95  		if tok == "NULLDESC" {
    96  			key = encoding.EncodeNullDescending(key)
    97  			continue
    98  		}
    99  
   100  		// We make a distinction between this and the interleave
   101  		// sentinel below.
   102  		if tok == "NOTNULLDESC" {
   103  			key = encoding.EncodeNotNullDescending(key)
   104  			continue
   105  		}
   106  
   107  		// Interleaved sentinel.
   108  		if tok == "#" {
   109  			key = encoding.EncodeNotNullDescending(key)
   110  			continue
   111  		}
   112  
   113  		// Assume any other value is an unsigned integer.
   114  		tokInt, err := strconv.ParseInt(tok, 10, 64)
   115  		if err != nil {
   116  			tb.Fatal(err)
   117  		}
   118  		key = encoding.EncodeVarintAscending(key, tokInt)
   119  	}
   120  
   121  	return key
   122  }
   123  
   124  // See CreateTestInterleavedHierarchy for the longest chain used for the short
   125  // format.
   126  var shortFormTables = [3]string{"parent1", "child1", "grandchild1"}
   127  
   128  // ShortToLongKeyFmt converts the short key format preferred in test cases
   129  //    /1/#/3/4
   130  // to its long form required by parseTestkey
   131  //    parent1/1/1/#/child1/1/3/4
   132  // The short key format can end in an interleave sentinel '#' (i.e. after
   133  // TightenEndKey).
   134  // The short key format can also be "/" or end in "#/" which will append
   135  // the parent's table/index info. without a trailing index column value.
   136  func ShortToLongKeyFmt(short string) string {
   137  	tableOrder := shortFormTables
   138  	curTableIdx := 0
   139  
   140  	var long []byte
   141  	tokens := strings.Split(short, "/")
   142  	// Verify short format starts with '/'.
   143  	if tokens[0] != "" {
   144  		panic("missing '/' token at the beginning of short format")
   145  	}
   146  	// Skip the first element since short format has starting '/'.
   147  	tokens = tokens[1:]
   148  
   149  	// Always append parent1.
   150  	long = append(long, []byte(fmt.Sprintf("/%s/1/", tableOrder[curTableIdx]))...)
   151  	curTableIdx++
   152  
   153  	for i, tok := range tokens {
   154  		// Permits ".../#/" to append table name without a value
   155  		if tok == "" {
   156  			continue
   157  		}
   158  
   159  		if tok == "#" {
   160  			long = append(long, []byte("#/")...)
   161  			// It's possible for the short-format to end with a #.
   162  			if i == len(tokens)-1 {
   163  				break
   164  			}
   165  
   166  			// New interleaved table and primary keys follow.
   167  			if curTableIdx >= len(tableOrder) {
   168  				panic("too many '#' tokens specified in short format (max 2 for child1 and 3 for grandchild1)")
   169  			}
   170  
   171  			long = append(long, []byte(fmt.Sprintf("%s/1/", tableOrder[curTableIdx]))...)
   172  			curTableIdx++
   173  
   174  			continue
   175  		}
   176  
   177  		long = append(long, []byte(fmt.Sprintf("%s/", tok))...)
   178  	}
   179  
   180  	// Remove the last '/'.
   181  	return string(long[:len(long)-1])
   182  }