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  }