-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
expression, executor: rewrite built-in func makeDate using new expression evaluation architecture #3533
expression, executor: rewrite built-in func makeDate using new expression evaluation architecture #3533
Changes from 8 commits
4fa9906
90c4e04
b37fe84
2070559
94ba710
828035c
8078486
45a0bcb
08b4faf
6e13eaf
a41094f
f76fa88
00b5d26
b82fb64
ae64575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2023,35 +2023,36 @@ type makeDateFunctionClass struct { | |
} | ||
|
||
func (c *makeDateFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) { | ||
sig := &builtinMakeDateSig{newBaseBuiltinFunc(args, ctx)} | ||
tp := types.NewFieldType(mysql.TypeDate) | ||
tp.Flen = mysql.MAX_DATE_WIDTH | ||
types.SetBinChsClnFlag(tp) | ||
bf, err := newBaseBuiltinFuncWithTp(args, tp, ctx, tpInt, tpInt) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
sig := &builtinMakeDateSig{baseTimeBuiltinFunc{bf}} | ||
return sig.setSelf(sig), errors.Trace(c.verifyArgs(args)) | ||
} | ||
|
||
type builtinMakeDateSig struct { | ||
baseBuiltinFunc | ||
baseTimeBuiltinFunc | ||
} | ||
|
||
// eval evals a builtinMakeDateSig. | ||
// evalTime evaluates a builtinMakeDateSig. | ||
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_makedate | ||
func (b *builtinMakeDateSig) eval(row []types.Datum) (d types.Datum, err error) { | ||
args, err := b.evalArgs(row) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
func (b *builtinMakeDateSig) evalTime(row []types.Datum) (d types.Time, isNull bool, err error) { | ||
args := b.getArgs() | ||
var year, dayOfYear int64 | ||
year, isNull, err = args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx) | ||
if isNull || err != nil { | ||
return d, isNull, errors.Trace(err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return d, true is clearer |
||
} | ||
if args[0].IsNull() || args[1].IsNull() { | ||
return | ||
} | ||
sc := b.ctx.GetSessionVars().StmtCtx | ||
year, err := args[0].ToInt64(sc) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
} | ||
dayOfYear, err := args[1].ToInt64(sc) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
dayOfYear, isNull, err = args[1].EvalInt(row, b.ctx.GetSessionVars().StmtCtx) | ||
if isNull || err != nil { | ||
return d, isNull, errors.Trace(err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. |
||
} | ||
if dayOfYear <= 0 || year < 0 || year > 9999 { | ||
return | ||
return d, true, nil | ||
} | ||
if year < 70 { | ||
year += 2000 | ||
|
@@ -2065,14 +2066,13 @@ func (b *builtinMakeDateSig) eval(row []types.Datum) (d types.Datum, err error) | |
} | ||
retTimestamp := types.TimestampDiff("DAY", types.ZeroDate, startTime) | ||
if retTimestamp == 0 { | ||
return d, errorOrWarning(types.ErrInvalidTimeFormat, b.ctx) | ||
return d, true, errorOrWarning(types.ErrInvalidTimeFormat, b.ctx) | ||
} | ||
ret := types.TimeFromDays(retTimestamp + dayOfYear - 1) | ||
if ret.IsZero() || ret.Time.Year() > 9999 { | ||
return | ||
return d, true, nil | ||
} | ||
d.SetMysqlTime(ret) | ||
return | ||
return ret, false, nil | ||
} | ||
|
||
type makeTimeFunctionClass struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,8 +18,11 @@ import ( | |
"strings" | ||
"time" | ||
|
||
"github.com/juju/errors" | ||
. "github.com/pingcap/check" | ||
"github.com/pingcap/tidb/ast" | ||
"github.com/pingcap/tidb/mysql" | ||
"github.com/pingcap/tidb/util/charset" | ||
"github.com/pingcap/tidb/util/mock" | ||
"github.com/pingcap/tidb/util/testleak" | ||
"github.com/pingcap/tidb/util/testutil" | ||
|
@@ -1203,42 +1206,56 @@ func (s *testEvaluatorSuite) TestTimestamp(c *C) { | |
|
||
func (s *testEvaluatorSuite) TestMakeDate(c *C) { | ||
defer testleak.AfterTest(c)() | ||
tbl := []struct { | ||
Args []interface{} | ||
Want interface{} | ||
cases := []struct { | ||
args []interface{} | ||
expected string | ||
isNil bool | ||
getErr bool | ||
}{ | ||
{[]interface{}{71, 1}, "1971-01-01"}, | ||
{[]interface{}{99, 1}, "1999-01-01"}, | ||
{[]interface{}{100, 1}, "0100-01-01"}, | ||
{[]interface{}{69, 1}, "2069-01-01"}, | ||
{[]interface{}{70, 1}, "1970-01-01"}, | ||
{[]interface{}{1000, 1}, "1000-01-01"}, | ||
{[]interface{}{-1, 3660}, nil}, | ||
{[]interface{}{10000, 3660}, nil}, | ||
{[]interface{}{2060, 2900025}, "9999-12-31"}, | ||
{[]interface{}{2060, 2900026}, nil}, | ||
{[]interface{}{nil, 2900025}, nil}, | ||
{[]interface{}{2060, nil}, nil}, | ||
{[]interface{}{nil, nil}, nil}, | ||
{[]interface{}{"71", 1}, "1971-01-01"}, | ||
{[]interface{}{71, "1"}, "1971-01-01"}, | ||
{[]interface{}{"71", "1"}, "1971-01-01"}, | ||
} | ||
Dtbl := tblToDtbl(tbl) | ||
maketime := funcs[ast.MakeDate] | ||
for idx, t := range Dtbl { | ||
f, err := maketime.getFunction(datumsToConstants(t["Args"]), s.ctx) | ||
c.Assert(err, IsNil) | ||
got, err := f.eval(nil) | ||
c.Assert(err, IsNil) | ||
if t["Want"][0].Kind() == types.KindNull { | ||
c.Assert(got.Kind(), Equals, types.KindNull, Commentf("[%v] - args:%v", idx, t["Args"])) | ||
{[]interface{}{71, 1}, "1971-01-01", false, false}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add test cases for float64 value |
||
{[]interface{}{99, 1}, "1999-01-01", false, false}, | ||
{[]interface{}{100, 1}, "0100-01-01", false, false}, | ||
{[]interface{}{69, 1}, "2069-01-01", false, false}, | ||
{[]interface{}{70, 1}, "1970-01-01", false, false}, | ||
{[]interface{}{1000, 1}, "1000-01-01", false, false}, | ||
{[]interface{}{-1, 3660}, "", true, false}, | ||
{[]interface{}{10000, 3660}, "", true, false}, | ||
{[]interface{}{2060, 2900025}, "9999-12-31", false, false}, | ||
{[]interface{}{2060, 2900026}, "", true, false}, | ||
{[]interface{}{"71", 1}, "1971-01-01", false, false}, | ||
{[]interface{}{71, "1"}, "1971-01-01", false, false}, | ||
{[]interface{}{"71", "1"}, "1971-01-01", false, false}, | ||
{[]interface{}{nil, 2900025}, "", true, false}, | ||
{[]interface{}{2060, nil}, "", true, false}, | ||
{[]interface{}{nil, nil}, "", true, false}, | ||
{[]interface{}{errors.New("must error"), errors.New("must error")}, "", false, true}, | ||
} | ||
|
||
for _, t := range cases { | ||
f, err := newFunctionForTest(s.ctx, ast.MakeDate, primitiveValsToConstants(t.args)...) | ||
c.Assert(err, IsNil) | ||
tp := f.GetType() | ||
c.Assert(tp.Tp, Equals, mysql.TypeDate) | ||
c.Assert(tp.Charset, Equals, charset.CharsetBin) | ||
c.Assert(tp.Collate, Equals, charset.CollationBin) | ||
c.Assert(tp.Flag, Equals, uint(mysql.BinaryFlag)) | ||
c.Assert(tp.Flen, Equals, mysql.MAX_DATE_WIDTH) | ||
d, err := f.Eval(nil) | ||
if t.getErr { | ||
c.Assert(err, NotNil) | ||
} else { | ||
want, err := t["Want"][0].ToString() | ||
c.Assert(err, IsNil) | ||
c.Assert(got.GetMysqlTime().String(), Equals, want, Commentf("[%v] - args:%v", idx, t["Args"])) | ||
if t.isNil { | ||
c.Assert(d.Kind(), Equals, types.KindNull) | ||
} else { | ||
c.Assert(d.GetMysqlTime().String(), Equals, t.expected) | ||
} | ||
} | ||
} | ||
|
||
f, err := funcs[ast.MakeDate].getFunction([]Expression{Zero, Zero}, s.ctx) | ||
c.Assert(err, IsNil) | ||
c.Assert(f.isDeterministic(), IsTrue) | ||
} | ||
|
||
func (s *testEvaluatorSuite) TestMakeTime(c *C) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,6 +193,11 @@ const ( | |
// AllPrivMask is the mask for PrivilegeType with all bits set to 1. | ||
const AllPrivMask = AllPriv - 1 | ||
|
||
const ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add comments for these const. |
||
// YYYY-MM-DD | ||
MAX_DATE_WIDTH = 10 | ||
) | ||
|
||
// Priv2UserCol is the privilege to mysql.user table column name. | ||
var Priv2UserCol = map[PrivilegeType]string{ | ||
CreatePriv: "Create_priv", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sc := b.ctx.GetSessionVars().StmtCtx
so we can use it at line 2050