Skip to content

Commit

Permalink
expression: not push invalid cast to tiflash (#28458) (#28654)
Browse files Browse the repository at this point in the history
  • Loading branch information
ti-srebot authored Oct 14, 2021
1 parent 873ba89 commit 30c970e
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 23 deletions.
6 changes: 3 additions & 3 deletions executor/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ func (s *testSuite5) TestShowWarningsForExprPushdown(c *C) {
tk.MustExec(testSQL)
tk.MustExec("explain select * from show_warnings_expr_pushdown where date_add(value, interval 1 day) = '2020-01-01'")
c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt) can not be pushed to tikv"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt, return type: datetime) can not be pushed to tikv"))
tk.MustExec("explain select max(date_add(value, interval 1 day)) from show_warnings_expr_pushdown group by a")
c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(2))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt) can not be pushed to tikv", "Warning|1105|Aggregation can not be pushed to tikv because arguments of AggFunc `max` contains unsupported exprs"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt, return type: datetime) can not be pushed to tikv", "Warning|1105|Aggregation can not be pushed to tikv because arguments of AggFunc `max` contains unsupported exprs"))
tk.MustExec("explain select max(a) from show_warnings_expr_pushdown group by date_add(value, interval 1 day)")
c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(2))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt) can not be pushed to tikv", "Warning|1105|Aggregation can not be pushed to tikv because groupByItems contain unsupported exprs"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1105|Scalar function 'date_add'(signature: AddDateDatetimeInt, return type: datetime) can not be pushed to tikv", "Warning|1105|Aggregation can not be pushed to tikv because groupByItems contain unsupported exprs"))
tk.MustExec("set tidb_opt_distinct_agg_push_down=0")
tk.MustExec("explain select max(distinct a) from show_warnings_expr_pushdown group by value")
c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1))
Expand Down
47 changes: 42 additions & 5 deletions expression/expr_to_pb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,8 @@ func (s *testEvaluatorSuite) TestExprPushDownToFlash(c *C) {
datetimeColumn := dg.genColumn(mysql.TypeDatetime, 6)
binaryStringColumn := dg.genColumn(mysql.TypeString, 7)
binaryStringColumn.RetType.Collate = charset.CollationBin
int32Column := dg.genColumn(mysql.TypeLong, 8)
float32Column := dg.genColumn(mysql.TypeFloat, 9)

function, err := NewFunction(mock.NewContext(), ast.JSONLength, types.NewFieldType(mysql.TypeLonglong), jsonColumn)
c.Assert(err, IsNil)
Expand Down Expand Up @@ -655,28 +657,31 @@ func (s *testEvaluatorSuite) TestExprPushDownToFlash(c *C) {
c.Assert(err, IsNil)
exprs = append(exprs, function)

validDecimalType := types.NewFieldType(mysql.TypeNewDecimal)
validDecimalType.Flen = 20
validDecimalType.Decimal = 2
// CastIntAsDecimal
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), intColumn)
function, err = NewFunction(mock.NewContext(), ast.Cast, validDecimalType, intColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// CastRealAsDecimal
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), realColumn)
function, err = NewFunction(mock.NewContext(), ast.Cast, validDecimalType, realColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// CastDecimalAsDecimal
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), decimalColumn)
function, err = NewFunction(mock.NewContext(), ast.Cast, validDecimalType, decimalColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// CastStringAsDecimal
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), stringColumn)
function, err = NewFunction(mock.NewContext(), ast.Cast, validDecimalType, stringColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// CastTimeAsDecimal
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), datetimeColumn)
function, err = NewFunction(mock.NewContext(), ast.Cast, validDecimalType, datetimeColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

Expand Down Expand Up @@ -940,6 +945,16 @@ func (s *testEvaluatorSuite) TestExprPushDownToFlash(c *C) {
c.Assert(function.(*ScalarFunction).Function.PbCode(), Equals, tipb.ScalarFuncSig_StrToDateDatetime)
exprs = append(exprs, function)

// cast Int32 to Int32
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeLong), int32Column)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// cast float32 to float32
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeFloat), float32Column)
c.Assert(err, IsNil)
exprs = append(exprs, function)

canPush := CanExprsPushDown(sc, exprs, client, kv.TiFlash)
c.Assert(canPush, Equals, true)

Expand Down Expand Up @@ -969,6 +984,28 @@ func (s *testEvaluatorSuite) TestExprPushDownToFlash(c *C) {
c.Assert(err, IsNil)
exprs = append(exprs, function)

// Cast to Int32: not supported
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeLong), stringColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// Cast to Float: not supported
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeFloat), intColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// Cast to invalid Decimal Type: not supported
function, err = NewFunction(mock.NewContext(), ast.Cast, types.NewFieldType(mysql.TypeNewDecimal), intColumn)
c.Assert(err, IsNil)
exprs = append(exprs, function)

// cast Int32 to UInt32
unsignedInt32Type := types.NewFieldType(mysql.TypeLong)
unsignedInt32Type.Flag = mysql.UnsignedFlag
function, err = NewFunction(mock.NewContext(), ast.Cast, unsignedInt32Type, int32Column)
c.Assert(err, IsNil)
exprs = append(exprs, function)

pushed, remained := PushDownExprs(sc, exprs, client, kv.TiFlash)
c.Assert(len(pushed), Equals, 0)
c.Assert(len(remained), Equals, len(exprs))
Expand Down
34 changes: 26 additions & 8 deletions expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,13 @@ func scalarExprSupportedByTiKV(sf *ScalarFunction) bool {
return false
}

func isValidTiFlashDecimalType(tp *types.FieldType) bool {
if tp.Tp != mysql.TypeNewDecimal {
return false
}
return tp.Flen > 0 && tp.Flen <= 65 && tp.Decimal >= 0 && tp.Decimal <= 30 && tp.Flen >= tp.Decimal
}

func scalarExprSupportedByFlash(function *ScalarFunction) bool {
switch function.FuncName.L {
case ast.Floor, ast.Ceil, ast.Ceiling:
Expand Down Expand Up @@ -1038,16 +1045,27 @@ func scalarExprSupportedByFlash(function *ScalarFunction) bool {
return true
}
case ast.Cast:
sourceType := function.GetArgs()[0].GetType()
retType := function.RetType
switch function.Function.PbCode() {
case tipb.ScalarFuncSig_CastIntAsTime:
case tipb.ScalarFuncSig_CastDecimalAsInt, tipb.ScalarFuncSig_CastIntAsInt, tipb.ScalarFuncSig_CastRealAsInt, tipb.ScalarFuncSig_CastTimeAsInt,
tipb.ScalarFuncSig_CastStringAsInt /*, tipb.ScalarFuncSig_CastDurationAsInt, tipb.ScalarFuncSig_CastJsonAsInt*/ :
// TiFlash cast only support cast to Int64 or the source type is the same as the target type
return (sourceType.Tp == retType.Tp && mysql.HasUnsignedFlag(sourceType.Flag) == mysql.HasUnsignedFlag(retType.Flag)) || retType.Tp == mysql.TypeLonglong
case tipb.ScalarFuncSig_CastIntAsReal, tipb.ScalarFuncSig_CastRealAsReal, tipb.ScalarFuncSig_CastStringAsReal: /*, tipb.ScalarFuncSig_CastDecimalAsReal,
tipb.ScalarFuncSig_CastDurationAsReal, tipb.ScalarFuncSig_CastTimeAsReal, tipb.ScalarFuncSig_CastJsonAsReal*/
// TiFlash cast only support cast to Float64 or the source type is the same as the target type
return sourceType.Tp == retType.Tp || retType.Tp == mysql.TypeDouble
case tipb.ScalarFuncSig_CastDecimalAsDecimal, tipb.ScalarFuncSig_CastIntAsDecimal, tipb.ScalarFuncSig_CastRealAsDecimal, tipb.ScalarFuncSig_CastTimeAsDecimal,
tipb.ScalarFuncSig_CastStringAsDecimal /*, tipb.ScalarFuncSig_CastDurationAsDecimal, tipb.ScalarFuncSig_CastJsonAsDecimal*/ :
return isValidTiFlashDecimalType(function.RetType)
case tipb.ScalarFuncSig_CastDecimalAsString, tipb.ScalarFuncSig_CastIntAsString, tipb.ScalarFuncSig_CastRealAsString, tipb.ScalarFuncSig_CastTimeAsString,
tipb.ScalarFuncSig_CastStringAsString /*, tipb.ScalarFuncSig_CastDurationAsString, tipb.ScalarFuncSig_CastJsonAsString*/ :
return true
case tipb.ScalarFuncSig_CastDecimalAsTime, tipb.ScalarFuncSig_CastIntAsTime, tipb.ScalarFuncSig_CastRealAsTime, tipb.ScalarFuncSig_CastTimeAsTime,
tipb.ScalarFuncSig_CastStringAsTime /*, tipb.ScalarFuncSig_CastDurationAsTime, tipb.ScalarFuncSig_CastJsonAsTime*/ :
// ban the function of casting year type as time type pushing down to tiflash because of https://github.com/pingcap/tidb/issues/26215
return function.GetArgs()[0].GetType().Tp != mysql.TypeYear
case tipb.ScalarFuncSig_CastIntAsInt, tipb.ScalarFuncSig_CastIntAsReal, tipb.ScalarFuncSig_CastIntAsDecimal, tipb.ScalarFuncSig_CastIntAsString,
tipb.ScalarFuncSig_CastRealAsInt, tipb.ScalarFuncSig_CastRealAsReal, tipb.ScalarFuncSig_CastRealAsDecimal, tipb.ScalarFuncSig_CastRealAsString, tipb.ScalarFuncSig_CastRealAsTime,
tipb.ScalarFuncSig_CastStringAsInt, tipb.ScalarFuncSig_CastStringAsReal, tipb.ScalarFuncSig_CastStringAsDecimal, tipb.ScalarFuncSig_CastStringAsString, tipb.ScalarFuncSig_CastStringAsTime,
tipb.ScalarFuncSig_CastDecimalAsInt /*, tipb.ScalarFuncSig_CastDecimalAsReal*/, tipb.ScalarFuncSig_CastDecimalAsDecimal, tipb.ScalarFuncSig_CastDecimalAsString, tipb.ScalarFuncSig_CastDecimalAsTime,
tipb.ScalarFuncSig_CastTimeAsInt /*, tipb.ScalarFuncSig_CastTimeAsReal*/, tipb.ScalarFuncSig_CastTimeAsDecimal, tipb.ScalarFuncSig_CastTimeAsTime, tipb.ScalarFuncSig_CastTimeAsString:
return true
}
case ast.DateAdd, ast.AddDate:
switch function.Function.PbCode() {
Expand Down Expand Up @@ -1180,7 +1198,7 @@ func canScalarFuncPushDown(scalarFunc *ScalarFunction, pc PbConverter, storeType
if storeType == kv.UnSpecified {
storageName = "storage layer"
}
pc.sc.AppendWarning(errors.New("Scalar function '" + scalarFunc.FuncName.L + "'(signature: " + scalarFunc.Function.PbCode().String() + ") can not be pushed to " + storageName))
pc.sc.AppendWarning(errors.New("Scalar function '" + scalarFunc.FuncName.L + "'(signature: " + scalarFunc.Function.PbCode().String() + ", return type: " + scalarFunc.RetType.CompactStr() + ") can not be pushed to " + storageName))
}
return false
}
Expand Down
6 changes: 3 additions & 3 deletions planner/core/testdata/enforce_mpp_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,11 @@
" └─TableFullScan_10 10000.00 cop[tiflash] table:t keep order:false, stats:pseudo"
],
"Warn": [
"Scalar function 'md5'(signature: MD5) can not be pushed to tiflash",
"Scalar function 'md5'(signature: MD5, return type: var_string(32)) can not be pushed to tiflash",
"Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs",
"Scalar function 'md5'(signature: MD5) can not be pushed to tiflash",
"Scalar function 'md5'(signature: MD5, return type: var_string(32)) can not be pushed to tiflash",
"Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs",
"Scalar function 'md5'(signature: MD5) can not be pushed to tiflash",
"Scalar function 'md5'(signature: MD5, return type: var_string(32)) can not be pushed to tiflash",
"Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs"
]
},
Expand Down
2 changes: 1 addition & 1 deletion planner/core/testdata/integration_serial_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "TestSelPushDownTiFlash",
"cases": [
"explain format = 'brief' select * from t where t.a > 1 and t.b = \"flash\" or t.a + 3 * t.a = 5",
"explain format = 'brief' select * from t where cast(t.a as float) + 3 = 5.1",
"explain format = 'brief' select * from t where cast(t.a as double) + 3 = 5.1",
"explain format = 'brief' select * from t where b > 'a' order by convert(b, unsigned) limit 2",
"explain format = 'brief' select * from t where b > 'a' order by b limit 2"
]
Expand Down
4 changes: 2 additions & 2 deletions planner/core/testdata/integration_serial_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
]
},
{
"SQL": "explain format = 'brief' select * from t where cast(t.a as float) + 3 = 5.1",
"SQL": "explain format = 'brief' select * from t where cast(t.a as double) + 3 = 5.1",
"Plan": [
"TableReader 8000.00 root data:Selection",
"└─Selection 8000.00 cop[tiflash] eq(plus(cast(test.t.a, float BINARY), 3), 5.1)",
"└─Selection 8000.00 cop[tiflash] eq(plus(cast(test.t.a, double BINARY), 3), 5.1)",
" └─TableFullScan 10000.00 cop[tiflash] table:t keep order:false, stats:pseudo"
]
},
Expand Down
2 changes: 1 addition & 1 deletion planner/core/testdata/plan_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,7 @@
" └─IndexFullScan 1.00 cop[tikv] table:tn, index:a(a, b, c, d) keep order:true, stats:pseudo"
],
"Warning": [
"Scalar function 'intdiv'(signature: IntDivideInt) can not be pushed to storage layer",
"Scalar function 'intdiv'(signature: IntDivideInt, return type: bigint(20)) can not be pushed to storage layer",
"[planner:1815]Optimizer Hint LIMIT_TO_COP is inapplicable"
]
},
Expand Down

0 comments on commit 30c970e

Please sign in to comment.