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 }