Skip to content

Commit

Permalink
expression: refactor builtin function RAND to be compatible with MySQL (
Browse files Browse the repository at this point in the history
  • Loading branch information
XuHuaiyu authored and ngaut committed Feb 25, 2019
1 parent db48da2 commit 225421b
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 15 deletions.
41 changes: 26 additions & 15 deletions expression/builtin_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,9 +966,23 @@ func (c *randFunctionClass) getFunction(ctx sessionctx.Context, args []Expressio
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETReal, argTps...)
bt := bf
if len(args) == 0 {
sig = &builtinRandSig{bt, nil}
seed := time.Now().UnixNano()
sig = &builtinRandSig{bt, rand.New(rand.NewSource(seed))}
} else if _, isConstant := args[0].(*Constant); isConstant {
// According to MySQL manual:
// If an integer argument N is specified, it is used as the seed value:
// With a constant initializer argument, the seed is initialized once
// when the statement is prepared, prior to execution.
seed, isNull, err := args[0].EvalInt(ctx, chunk.Row{})
if err != nil {
return nil, err
}
if isNull {
seed = time.Now().UnixNano()
}
sig = &builtinRandSig{bt, rand.New(rand.NewSource(seed))}
} else {
sig = &builtinRandWithSeedSig{bt, nil}
sig = &builtinRandWithSeedSig{bt}
}
return sig, nil
}
Expand All @@ -987,19 +1001,15 @@ func (b *builtinRandSig) Clone() builtinFunc {
// evalReal evals RAND().
// See https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_rand
func (b *builtinRandSig) evalReal(row chunk.Row) (float64, bool, error) {
if b.randGen == nil {
b.randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
}
return b.randGen.Float64(), false, nil
}

type builtinRandWithSeedSig struct {
baseBuiltinFunc
randGen *rand.Rand
}

func (b *builtinRandWithSeedSig) Clone() builtinFunc {
newSig := &builtinRandWithSeedSig{randGen: b.randGen}
newSig := &builtinRandWithSeedSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
Expand All @@ -1011,15 +1021,16 @@ func (b *builtinRandWithSeedSig) evalReal(row chunk.Row) (float64, bool, error)
if err != nil {
return 0, true, errors.Trace(err)
}
if b.randGen == nil {
if isNull {
// When seed is NULL, it is equal to RAND().
b.randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
} else {
b.randGen = rand.New(rand.NewSource(seed))
}
// b.args[0] is promised to be a non-constant(such as a column name) in
// builtinRandWithSeedSig, the seed is initialized with the value for each
// invocation of RAND().
var randGen *rand.Rand
if isNull {
randGen = rand.New(rand.NewSource(time.Now().UnixNano()))
} else {
randGen = rand.New(rand.NewSource(seed))
}
return b.randGen.Float64(), false, nil
return randGen.Float64(), false, nil
}

type powFunctionClass struct {
Expand Down
7 changes: 7 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,13 @@ func (s *testIntegrationSuite) TestMathBuiltin(c *C) {
// for radians
result = tk.MustQuery("SELECT radians(1.0), radians(pi()), radians(pi()/2), radians(180), radians(1.009);")
result.Check(testkit.Rows("0.017453292519943295 0.05483113556160754 0.02741556778080377 3.141592653589793 0.01761037215262278"))

// for rand
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int)")
tk.MustExec("insert into t values(1),(2),(3)")
tk.MustQuery("select rand(a) from t").Check(testkit.Rows("0.6046602879796196", "0.16729663442585624", "0.7199826688373036"))
tk.MustQuery("select rand(1), rand(2), rand(3)").Check(testkit.Rows("0.6046602879796196 0.16729663442585624 0.7199826688373036"))
}

func (s *testIntegrationSuite) TestStringBuiltin(c *C) {
Expand Down

0 comments on commit 225421b

Please sign in to comment.