Skip to content

Commit

Permalink
expression: rewrite builtin function: ISNULL (pingcap#4127)
Browse files Browse the repository at this point in the history
  • Loading branch information
breezewish authored and shenli committed Aug 10, 2017
1 parent c51d0f5 commit c301d9c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 20 deletions.
123 changes: 108 additions & 15 deletions expression/builtin_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ var (
_ builtinFunc = &builtinIsTrueOpSig{}
_ builtinFunc = &builtinUnaryOpSig{}
_ builtinFunc = &builtinUnaryMinusIntSig{}
_ builtinFunc = &builtinIsNullSig{}
_ builtinFunc = &builtinDecimalIsNullSig{}
_ builtinFunc = &builtinDurationIsNullSig{}
_ builtinFunc = &builtinIntIsNullSig{}
_ builtinFunc = &builtinRealIsNullSig{}
_ builtinFunc = &builtinStringIsNullSig{}
_ builtinFunc = &builtinTimeIsNullSig{}
_ builtinFunc = &builtinUnaryNotSig{}
)

Expand Down Expand Up @@ -659,25 +664,113 @@ type isNullFunctionClass struct {
}

func (c *isNullFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) {
sig := &builtinIsNullSig{newBaseBuiltinFunc(args, ctx)}
return sig.setSelf(sig), errors.Trace(c.verifyArgs(args))
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
tc := args[0].GetType().ToClass()
var argTp evalTp
switch tc {
case types.ClassInt:
argTp = tpInt
case types.ClassDecimal:
argTp = tpDecimal
case types.ClassReal:
argTp = tpReal
default:
tp := args[0].GetType().Tp
if types.IsTypeTime(tp) {
argTp = tpTime
} else if tp == mysql.TypeDuration {
argTp = tpDuration
} else {
argTp = tpString
}
}
bf, err := newBaseBuiltinFuncWithTp(args, ctx, tpInt, argTp)
if err != nil {
return nil, errors.Trace(err)
}
bf.tp.Flen = 1
var sig builtinFunc
switch argTp {
case tpInt:
sig = &builtinIntIsNullSig{baseIntBuiltinFunc{bf}}
case tpDecimal:
sig = &builtinDecimalIsNullSig{baseIntBuiltinFunc{bf}}
case tpReal:
sig = &builtinRealIsNullSig{baseIntBuiltinFunc{bf}}
case tpTime:
sig = &builtinTimeIsNullSig{baseIntBuiltinFunc{bf}}
case tpDuration:
sig = &builtinDurationIsNullSig{baseIntBuiltinFunc{bf}}
case tpString:
sig = &builtinStringIsNullSig{baseIntBuiltinFunc{bf}}
default:
panic("unexpected evalTp")
}
return sig.setSelf(sig), nil
}

type builtinIsNullSig struct {
baseBuiltinFunc
type builtinDecimalIsNullSig struct {
baseIntBuiltinFunc
}

// eval evals a builtinIsNullSig.
// See https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_isnull
func (b *builtinIsNullSig) eval(row []types.Datum) (d types.Datum, err error) {
args, err := b.evalArgs(row)
func evalIsNull(isNull bool, err error) (int64, bool, error) {
if err != nil {
return types.Datum{}, errors.Trace(err)
return 0, true, errors.Trace(err)
}
if args[0].IsNull() {
d.SetInt64(1)
} else {
d.SetInt64(0)
if isNull {
return 1, false, nil
}
return d, nil
return 0, false, nil
}

func (b *builtinDecimalIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalDecimal(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}

type builtinDurationIsNullSig struct {
baseIntBuiltinFunc
}

func (b *builtinDurationIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalDuration(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}

type builtinIntIsNullSig struct {
baseIntBuiltinFunc
}

func (b *builtinIntIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}

type builtinRealIsNullSig struct {
baseIntBuiltinFunc
}

func (b *builtinRealIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalReal(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}

type builtinStringIsNullSig struct {
baseIntBuiltinFunc
}

func (b *builtinStringIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}

type builtinTimeIsNullSig struct {
baseIntBuiltinFunc
}

func (b *builtinTimeIsNullSig) evalInt(row []types.Datum) (int64, bool, error) {
_, isNull, err := b.args[0].EvalTime(row, b.ctx.GetSessionVars().StmtCtx)
return evalIsNull(isNull, err)
}
10 changes: 5 additions & 5 deletions expression/distsql_builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,13 +498,13 @@ func (s *testEvalSuite) TestEvalIsNull(c *C) {
sc := new(variable.StatementContext)
for _, tt := range tests {
expr, err := PBToExpr(tt.expr, nil, sc)
c.Assert(err, IsNil)
c.Assert(err, IsNil, Commentf("%v", tt))
result, err := expr.Eval(nil)
c.Assert(err, IsNil)
c.Assert(result.Kind(), Equals, tt.result.Kind())
c.Assert(err, IsNil, Commentf("%v", tt))
c.Assert(result.Kind(), Equals, tt.result.Kind(), Commentf("%v", tt))
cmp, err := result.CompareDatum(sc, tt.result)
c.Assert(err, IsNil)
c.Assert(cmp, Equals, 0)
c.Assert(err, IsNil, Commentf("%v", tt))
c.Assert(cmp, Equals, 0, Commentf("%v", tt))
}
}

Expand Down
7 changes: 7 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,13 @@ func (s *testIntegrationSuite) TestBuiltin(c *C) {
result = tk.MustQuery("select cast(a as json), cast(b as json), cast(c as json), cast(d as json), cast(e as json) from t")
result.Check(testkit.Rows(`"12.3" 1.23 "2017-01-01 12:12:12.000000" "12:12:12.000000" 123`))

// for ISNULL
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int, c int, d char(10), e datetime, f float, g decimal(10, 3))")
tk.MustExec("insert t values (1, 0, null, null, null, null, null)")
result = tk.MustQuery("select ISNULL(a), ISNULL(b), ISNULL(c), ISNULL(d), ISNULL(e), ISNULL(f), ISNULL(g) from t")
result.Check(testkit.Rows("0 0 1 1 1 1 1"))

// fix issue #3942
result = tk.MustQuery("select cast('-24 100:00:00' as time);")
result.Check(testkit.Rows("-676:00:00"))
Expand Down
22 changes: 22 additions & 0 deletions plan/typeinfer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (s *testPlanSuite) TestInferType(c *C) {
tests = append(tests, s.createTestCase4Aggregations()...)
tests = append(tests, s.createTestCase4InfoFunc()...)
tests = append(tests, s.createTestCase4EncryptionFuncs()...)
tests = append(tests, s.createTestCase4CompareFuncs()...)
tests = append(tests, s.createTestCase4Miscellaneous()...)

for _, tt := range tests {
Expand Down Expand Up @@ -526,6 +527,27 @@ func (s *testPlanSuite) createTestCase4EncryptionFuncs() []typeInferTestCase {
}
}

func (s *testPlanSuite) createTestCase4CompareFuncs() []typeInferTestCase {
return []typeInferTestCase{
{"isnull(c_int )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_bigint )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_float )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_double )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_decimal )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_datetime )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_time )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_timestamp)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_char )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_varchar )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_text )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_binary )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_varbinary)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_blob )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_set )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
{"isnull(c_enum )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0},
}
}

func (s *testPlanSuite) createTestCase4Miscellaneous() []typeInferTestCase {
return []typeInferTestCase{
{"sleep(c_int)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0},
Expand Down

0 comments on commit c301d9c

Please sign in to comment.