github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/internal/rsg/rsg.go (about) 1 // Copyright 2016 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 rsg 12 13 import ( 14 "fmt" 15 "math" 16 "math/rand" 17 "strings" 18 19 "github.com/cockroachdb/cockroach/pkg/internal/rsg/yacc" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 24 ) 25 26 // RSG is a random syntax generator. 27 type RSG struct { 28 Rnd *rand.Rand 29 30 lock syncutil.Mutex 31 seen map[string]bool 32 prods map[string][]*yacc.ExpressionNode 33 } 34 35 // NewRSG creates a random syntax generator from the given random seed and 36 // yacc file. 37 func NewRSG(seed int64, y string, allowDuplicates bool) (*RSG, error) { 38 tree, err := yacc.Parse("sql", y) 39 if err != nil { 40 return nil, err 41 } 42 rsg := RSG{ 43 Rnd: rand.New(&lockedSource{src: rand.NewSource(seed).(rand.Source64)}), 44 prods: make(map[string][]*yacc.ExpressionNode), 45 } 46 if !allowDuplicates { 47 rsg.seen = make(map[string]bool) 48 } 49 for _, prod := range tree.Productions { 50 rsg.prods[prod.Name] = prod.Expressions 51 } 52 return &rsg, nil 53 } 54 55 // Generate generates a unique random syntax from the root node. At most depth 56 // levels of token expansion are performed. An empty string is returned on 57 // error or if depth is exceeded. Generate is safe to call from mulitipule 58 // goroutines. If Generate is called more times than it can generate unique 59 // output, it will block forever. 60 func (r *RSG) Generate(root string, depth int) string { 61 for i := 0; i < 100000; i++ { 62 s := strings.Join(r.generate(root, depth), " ") 63 if r.seen != nil { 64 r.lock.Lock() 65 if !r.seen[s] { 66 r.seen[s] = true 67 } else { 68 s = "" 69 } 70 r.lock.Unlock() 71 } 72 if s != "" { 73 s = strings.Replace(s, "_LA", "", -1) 74 s = strings.Replace(s, " AS OF SYSTEM TIME \"string\"", "", -1) 75 return s 76 } 77 } 78 panic("couldn't find unique string") 79 } 80 81 func (r *RSG) generate(root string, depth int) []string { 82 // Initialize to an empty slice instead of nil because nil is the signal 83 // that the depth has been exceeded. 84 ret := make([]string, 0) 85 prods := r.prods[root] 86 if len(prods) == 0 { 87 return []string{root} 88 } 89 prod := prods[r.Intn(len(prods))] 90 for _, item := range prod.Items { 91 switch item.Typ { 92 case yacc.TypLiteral: 93 v := item.Value[1 : len(item.Value)-1] 94 ret = append(ret, v) 95 case yacc.TypToken: 96 var v []string 97 switch item.Value { 98 case "IDENT": 99 v = []string{"ident"} 100 case "c_expr": 101 v = r.generate(item.Value, 30) 102 case "SCONST": 103 v = []string{`'string'`} 104 case "ICONST": 105 v = []string{fmt.Sprint(r.Intn(1000) - 500)} 106 case "FCONST": 107 v = []string{fmt.Sprint(r.Float64())} 108 case "BCONST": 109 v = []string{`b'bytes'`} 110 case "BITCONST": 111 v = []string{`B'10010'`} 112 case "substr_from": 113 v = []string{"FROM", `'string'`} 114 case "substr_for": 115 v = []string{"FOR", `'string'`} 116 case "overlay_placing": 117 v = []string{"PLACING", `'string'`} 118 default: 119 if depth == 0 { 120 return nil 121 } 122 v = r.generate(item.Value, depth-1) 123 } 124 if v == nil { 125 return nil 126 } 127 ret = append(ret, v...) 128 default: 129 panic("unknown item type") 130 } 131 } 132 return ret 133 } 134 135 // Intn returns a random int. 136 func (r *RSG) Intn(n int) int { 137 return r.Rnd.Intn(n) 138 } 139 140 // Int63 returns a random int64. 141 func (r *RSG) Int63() int64 { 142 return r.Rnd.Int63() 143 } 144 145 // Float64 returns a random float. It is sometimes +/-Inf, NaN, and attempts to 146 // be distributed among very small, large, and normal scale numbers. 147 func (r *RSG) Float64() float64 { 148 v := r.Rnd.Float64()*2 - 1 149 switch r.Rnd.Intn(10) { 150 case 0: 151 v = 0 152 case 1: 153 v = math.Inf(1) 154 case 2: 155 v = math.Inf(-1) 156 case 3: 157 v = math.NaN() 158 case 4, 5: 159 i := r.Rnd.Intn(50) 160 v *= math.Pow10(i) 161 case 6, 7: 162 i := r.Rnd.Intn(50) 163 v *= math.Pow10(-i) 164 } 165 return v 166 } 167 168 // GenerateRandomArg generates a random, valid, SQL function argument of 169 // the specified type. 170 func (r *RSG) GenerateRandomArg(typ *types.T) string { 171 switch r.Intn(20) { 172 case 0: 173 return "NULL" 174 case 1: 175 return fmt.Sprintf("NULL::%s", typ) 176 case 2: 177 return fmt.Sprintf("(SELECT NULL)::%s", typ) 178 } 179 180 r.lock.Lock() 181 datum := sqlbase.RandDatumWithNullChance(r.Rnd, typ, 0) 182 r.lock.Unlock() 183 184 return tree.Serialize(datum) 185 } 186 187 // lockedSource is a thread safe math/rand.Source. See math/rand/rand.go. 188 type lockedSource struct { 189 lk syncutil.Mutex 190 src rand.Source64 191 } 192 193 func (r *lockedSource) Int63() (n int64) { 194 r.lk.Lock() 195 n = r.src.Int63() 196 r.lk.Unlock() 197 return 198 } 199 200 func (r *lockedSource) Uint64() (n uint64) { 201 r.lk.Lock() 202 n = r.src.Uint64() 203 r.lk.Unlock() 204 return 205 } 206 207 func (r *lockedSource) Seed(seed int64) { 208 r.lk.Lock() 209 r.src.Seed(seed) 210 r.lk.Unlock() 211 }