github.com/vedadiyan/sqlparser@v1.0.0/pkg/sqlparser/random_expr.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sqlparser 18 19 import ( 20 "fmt" 21 "math/rand" 22 ) 23 24 // This file is used to generate random expressions to be used for testing 25 26 func newGenerator(seed int64, maxDepth int) *generator { 27 g := generator{ 28 seed: seed, 29 r: rand.New(rand.NewSource(seed)), 30 maxDepth: maxDepth, 31 } 32 return &g 33 } 34 35 type generator struct { 36 seed int64 37 r *rand.Rand 38 depth int 39 maxDepth int 40 } 41 42 // enter should be called whenever we are producing an intermediate node. it should be followed by a `defer g.exit()` 43 func (g *generator) enter() { 44 g.depth++ 45 } 46 47 // exit should be called when exiting an intermediate node 48 func (g *generator) exit() { 49 g.depth-- 50 } 51 52 // atMaxDepth returns true if we have reached the maximum allowed depth or the expression tree 53 func (g *generator) atMaxDepth() bool { 54 return g.depth >= g.maxDepth 55 } 56 57 /* 58 Creates a random expression. It builds an expression tree using the following constructs: 59 - true/false 60 - AND/OR/NOT 61 - string literalrs, numeric literals (-/+ 1000) 62 - =, >, <, >=, <=, <=>, != 63 - &, |, ^, +, -, *, /, div, %, <<, >> 64 - IN, BETWEEN and CASE 65 - IS NULL, IS NOT NULL, IS TRUE, IS NOT TRUE, IS FALSE, IS NOT FALSE 66 67 Note: It's important to update this method so that it produces all expressions that need precedence checking. 68 It's currently missing function calls and string operators 69 */ 70 func (g *generator) expression() Expr { 71 if g.randomBool() { 72 return g.booleanExpr() 73 } 74 options := []exprF{ 75 func() Expr { return g.intExpr() }, 76 func() Expr { return g.stringExpr() }, 77 func() Expr { return g.booleanExpr() }, 78 } 79 80 return g.randomOf(options) 81 } 82 83 func (g *generator) booleanExpr() Expr { 84 if g.atMaxDepth() { 85 return g.booleanLiteral() 86 } 87 88 options := []exprF{ 89 func() Expr { return g.andExpr() }, 90 func() Expr { return g.xorExpr() }, 91 func() Expr { return g.orExpr() }, 92 func() Expr { return g.comparison(g.intExpr) }, 93 func() Expr { return g.comparison(g.stringExpr) }, 94 //func() Expr { return g.comparison(g.booleanExpr) }, // this is not accepted by the parser 95 func() Expr { return g.inExpr() }, 96 func() Expr { return g.between() }, 97 func() Expr { return g.isExpr() }, 98 func() Expr { return g.notExpr() }, 99 func() Expr { return g.likeExpr() }, 100 } 101 102 return g.randomOf(options) 103 } 104 105 func (g *generator) intExpr() Expr { 106 if g.atMaxDepth() { 107 return g.intLiteral() 108 } 109 110 options := []exprF{ 111 func() Expr { return g.arithmetic() }, 112 func() Expr { return g.intLiteral() }, 113 func() Expr { return g.caseExpr(g.intExpr) }, 114 } 115 116 return g.randomOf(options) 117 } 118 119 func (g *generator) booleanLiteral() Expr { 120 return BoolVal(g.randomBool()) 121 } 122 123 func (g *generator) randomBool() bool { 124 return g.r.Float32() < 0.5 125 } 126 127 func (g *generator) intLiteral() Expr { 128 t := fmt.Sprintf("%d", g.r.Intn(1000)-g.r.Intn((1000))) 129 130 return NewIntLiteral(t) 131 } 132 133 var words = []string{"ox", "ant", "ape", "asp", "bat", "bee", "boa", "bug", "cat", "cod", "cow", "cub", "doe", "dog", "eel", "eft", "elf", "elk", "emu", "ewe", "fly", "fox", "gar", "gnu", "hen", "hog", "imp", "jay", "kid", "kit", "koi", "lab", "man", "owl", "pig", "pug", "pup", "ram", "rat", "ray", "yak", "bass", "bear", "bird", "boar", "buck", "bull", "calf", "chow", "clam", "colt", "crab", "crow", "dane", "deer", "dodo", "dory", "dove", "drum", "duck", "fawn", "fish", "flea", "foal", "fowl", "frog", "gnat", "goat", "grub", "gull", "hare", "hawk", "ibex", "joey", "kite", "kiwi", "lamb", "lark", "lion", "loon", "lynx", "mako", "mink", "mite", "mole", "moth", "mule", "mutt", "newt", "orca", "oryx", "pika", "pony", "puma", "seal", "shad", "slug", "sole", "stag", "stud", "swan", "tahr", "teal", "tick", "toad", "tuna", "wasp", "wolf", "worm", "wren", "yeti", "adder", "akita", "alien", "aphid", "bison", "boxer", "bream", "bunny", "burro", "camel", "chimp", "civet", "cobra", "coral", "corgi", "crane", "dingo", "drake", "eagle", "egret", "filly", "finch", "gator", "gecko", "ghost", "ghoul", "goose", "guppy", "heron", "hippo", "horse", "hound", "husky", "hyena", "koala", "krill", "leech", "lemur", "liger", "llama", "louse", "macaw", "midge", "molly", "moose", "moray", "mouse", "panda", "perch", "prawn", "quail", "racer", "raven", "rhino", "robin", "satyr", "shark", "sheep", "shrew", "skink", "skunk", "sloth", "snail", "snake", "snipe", "squid", "stork", "swift", "swine", "tapir", "tetra", "tiger", "troll", "trout", "viper", "wahoo", "whale", "zebra", "alpaca", "amoeba", "baboon", "badger", "beagle", "bedbug", "beetle", "bengal", "bobcat", "caiman", "cattle", "cicada", "collie", "condor", "cougar", "coyote", "dassie", "donkey", "dragon", "earwig", "falcon", "feline", "ferret", "gannet", "gibbon", "glider", "goblin", "gopher", "grouse", "guinea", "hermit", "hornet", "iguana", "impala", "insect", "jackal", "jaguar", "jennet", "kitten", "kodiak", "lizard", "locust", "maggot", "magpie", "mammal", "mantis", "marlin", "marmot", "marten", "martin", "mayfly", "minnow", "monkey", "mullet", "muskox", "ocelot", "oriole", "osprey", "oyster", "parrot", "pigeon", "piglet", "poodle", "possum", "python", "quagga", "rabbit", "raptor", "rodent", "roughy", "salmon", "sawfly", "serval", "shiner", "shrimp", "spider", "sponge", "tarpon", "thrush", "tomcat", "toucan", "turkey", "turtle", "urchin", "vervet", "walrus", "weasel", "weevil", "wombat", "anchovy", "anemone", "bluejay", "buffalo", "bulldog", "buzzard", "caribou", "catfish", "chamois", "cheetah", "chicken", "chigger", "cowbird", "crappie", "crawdad", "cricket", "dogfish", "dolphin", "firefly", "garfish", "gazelle", "gelding", "giraffe", "gobbler", "gorilla", "goshawk", "grackle", "griffon", "grizzly", "grouper", "haddock", "hagfish", "halibut", "hamster", "herring", "jackass", "javelin", "jawfish", "jaybird", "katydid", "ladybug", "lamprey", "lemming", "leopard", "lioness", "lobster", "macaque", "mallard", "mammoth", "manatee", "mastiff", "meerkat", "mollusk", "monarch", "mongrel", "monitor", "monster", "mudfish", "muskrat", "mustang", "narwhal", "oarfish", "octopus", "opossum", "ostrich", "panther", "peacock", "pegasus", "pelican", "penguin", "phoenix", "piranha", "polecat", "primate", "quetzal", "raccoon", "rattler", "redbird", "redfish", "reptile", "rooster", "sawfish", "sculpin", "seagull", "skylark", "snapper", "spaniel", "sparrow", "sunbeam", "sunbird", "sunfish", "tadpole", "termite", "terrier", "unicorn", "vulture", "wallaby", "walleye", "warthog", "whippet", "wildcat", "aardvark", "airedale", "albacore", "anteater", "antelope", "arachnid", "barnacle", "basilisk", "blowfish", "bluebird", "bluegill", "bonefish", "bullfrog", "cardinal", "chipmunk", "cockatoo", "crayfish", "dinosaur", "doberman", "duckling", "elephant", "escargot", "flamingo", "flounder", "foxhound", "glowworm", "goldfish", "grubworm", "hedgehog", "honeybee", "hookworm", "humpback", "kangaroo", "killdeer", "kingfish", "labrador", "lacewing", "ladybird", "lionfish", "longhorn", "mackerel", "malamute", "marmoset", "mastodon", "moccasin", "mongoose", "monkfish", "mosquito", "pangolin", "parakeet", "pheasant", "pipefish", "platypus", "polliwog", "porpoise", "reindeer", "ringtail", "sailfish", "scorpion", "seahorse", "seasnail", "sheepdog", "shepherd", "silkworm", "squirrel", "stallion", "starfish", "starling", "stingray", "stinkbug", "sturgeon", "terrapin", "titmouse", "tortoise", "treefrog", "werewolf", "woodcock"} 134 135 func (g *generator) stringLiteral() Expr { 136 return NewStrLiteral(g.randomOfS(words)) 137 } 138 139 func (g *generator) stringExpr() Expr { 140 if g.atMaxDepth() { 141 return g.stringLiteral() 142 } 143 144 options := []exprF{ 145 func() Expr { return g.stringLiteral() }, 146 func() Expr { return g.caseExpr(g.stringExpr) }, 147 } 148 149 return g.randomOf(options) 150 } 151 152 func (g *generator) likeExpr() Expr { 153 g.enter() 154 defer g.exit() 155 return &ComparisonExpr{ 156 Operator: LikeOp, 157 Left: g.stringExpr(), 158 Right: g.stringExpr(), 159 } 160 } 161 162 var comparisonOps = []ComparisonExprOperator{EqualOp, LessThanOp, GreaterThanOp, LessEqualOp, GreaterEqualOp, NotEqualOp, NullSafeEqualOp} 163 164 func (g *generator) comparison(f func() Expr) Expr { 165 g.enter() 166 defer g.exit() 167 168 cmp := &ComparisonExpr{ 169 Operator: comparisonOps[g.r.Intn(len(comparisonOps))], 170 Left: f(), 171 Right: f(), 172 } 173 return cmp 174 } 175 176 func (g *generator) caseExpr(valueF func() Expr) Expr { 177 g.enter() 178 defer g.exit() 179 180 var exp Expr 181 var elseExpr Expr 182 if g.randomBool() { 183 exp = valueF() 184 } 185 if g.randomBool() { 186 elseExpr = valueF() 187 } 188 189 size := g.r.Intn(5) + 2 190 var whens []*When 191 for i := 0; i < size; i++ { 192 var cond Expr 193 if exp == nil { 194 cond = g.booleanExpr() 195 } else { 196 cond = g.expression() 197 } 198 199 whens = append(whens, &When{ 200 Cond: cond, 201 Val: g.expression(), 202 }) 203 } 204 205 return &CaseExpr{ 206 Expr: exp, 207 Whens: whens, 208 Else: elseExpr, 209 } 210 } 211 212 var arithmeticOps = []BinaryExprOperator{BitAndOp, BitOrOp, BitXorOp, PlusOp, MinusOp, MultOp, DivOp, IntDivOp, ModOp, ShiftRightOp, ShiftLeftOp} 213 214 func (g *generator) arithmetic() Expr { 215 g.enter() 216 defer g.exit() 217 218 op := arithmeticOps[g.r.Intn(len(arithmeticOps))] 219 220 return &BinaryExpr{ 221 Operator: op, 222 Left: g.intExpr(), 223 Right: g.intExpr(), 224 } 225 } 226 227 type exprF func() Expr 228 229 func (g *generator) randomOf(options []exprF) Expr { 230 return options[g.r.Intn(len(options))]() 231 } 232 233 func (g *generator) randomOfS(options []string) string { 234 return options[g.r.Intn(len(options))] 235 } 236 237 func (g *generator) andExpr() Expr { 238 g.enter() 239 defer g.exit() 240 return &AndExpr{ 241 Left: g.booleanExpr(), 242 Right: g.booleanExpr(), 243 } 244 } 245 246 func (g *generator) orExpr() Expr { 247 g.enter() 248 defer g.exit() 249 return &OrExpr{ 250 Left: g.booleanExpr(), 251 Right: g.booleanExpr(), 252 } 253 } 254 255 func (g *generator) xorExpr() Expr { 256 g.enter() 257 defer g.exit() 258 return &XorExpr{ 259 Left: g.booleanExpr(), 260 Right: g.booleanExpr(), 261 } 262 } 263 264 func (g *generator) notExpr() Expr { 265 g.enter() 266 defer g.exit() 267 return &NotExpr{g.booleanExpr()} 268 } 269 270 func (g *generator) inExpr() Expr { 271 g.enter() 272 defer g.exit() 273 274 expr := g.intExpr() 275 size := g.r.Intn(5) + 2 276 tuples := ValTuple{} 277 for i := 0; i < size; i++ { 278 tuples = append(tuples, g.intExpr()) 279 } 280 op := InOp 281 if g.randomBool() { 282 op = NotInOp 283 } 284 285 return &ComparisonExpr{ 286 Operator: op, 287 Left: expr, 288 Right: tuples, 289 } 290 } 291 292 func (g *generator) between() Expr { 293 g.enter() 294 defer g.exit() 295 296 var IsBetween bool 297 if g.randomBool() { 298 IsBetween = true 299 } else { 300 IsBetween = false 301 } 302 303 return &BetweenExpr{ 304 IsBetween: IsBetween, 305 Left: g.intExpr(), 306 From: g.intExpr(), 307 To: g.intExpr(), 308 } 309 } 310 311 func (g *generator) isExpr() Expr { 312 g.enter() 313 defer g.exit() 314 315 ops := []IsExprOperator{IsNullOp, IsNotNullOp, IsTrueOp, IsNotTrueOp, IsFalseOp, IsNotFalseOp} 316 317 return &IsExpr{ 318 Right: ops[g.r.Intn(len(ops))], 319 Left: g.booleanExpr(), 320 } 321 }