diff --git a/executor/show_test.go b/executor/show_test.go index 6cbecb365b152..2c031f7b81204 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -139,6 +139,16 @@ func (s *testSuite5) TestShowErrors(c *C) { tk.MustQuery("show errors").Check(testutil.RowsWithSep("|", "Error|1050|Table 'test.show_errors' already exists")) } +func (s *testSuite5) TestShowWarningsForExprPushdown(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + testSQL := `create table if not exists show_warnings_expr_pushdown (a int, value date)` + 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")) +} + func (s *testSuite5) TestShowGrantsPrivilege(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("create user show_grants") diff --git a/expression/expression.go b/expression/expression.go index 8eefc711530c7..6370926f2dd4d 100644 --- a/expression/expression.go +++ b/expression/expression.go @@ -1173,6 +1173,13 @@ func canScalarFuncPushDown(scalarFunc *ScalarFunction, pc PbConverter, storeType // Check whether this function can be pushed. if !canFuncBePushed(scalarFunc, storeType) { + if pc.sc.InExplainStmt { + storageName := storeType.Name() + 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)) + } return false } @@ -1196,6 +1203,9 @@ func canScalarFuncPushDown(scalarFunc *ScalarFunction, pc PbConverter, storeType func canExprPushDown(expr Expression, pc PbConverter, storeType kv.StoreType) bool { if storeType == kv.TiFlash && expr.GetType().Tp == mysql.TypeDuration { + if pc.sc.InExplainStmt { + pc.sc.AppendWarning(errors.New("Expr '" + expr.String() + "' can not be pushed to TiFlash because it contains Duration type")) + } return false } switch x := expr.(type) { diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index 38bf9910e9b9e..bfc22a4b8bd88 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -974,7 +974,7 @@ func (s *testPlanSuite) TestLimitToCopHint(c *C) { output []struct { SQL string Plan []string - Warning string + Warning []string } ) @@ -991,15 +991,20 @@ func (s *testPlanSuite) TestLimitToCopHint(c *C) { warnings := tk.Se.GetSessionVars().StmtCtx.GetWarnings() s.testData.OnRecord(func() { if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() + output[i].Warning = make([]string, len(warnings)) + for j, warning := range warnings { + output[i].Warning[j] = warning.Err.Error() + } } }) - if output[i].Warning == "" { + if len(output[i].Warning) == 0 { c.Assert(len(warnings), Equals, 0, comment) } else { - c.Assert(len(warnings), Equals, 1, comment) - c.Assert(warnings[0].Level, Equals, stmtctx.WarnLevelWarning, comment) - c.Assert(warnings[0].Err.Error(), Equals, output[i].Warning, comment) + c.Assert(len(warnings), Equals, len(output[i].Warning), comment) + for j, warning := range warnings { + c.Assert(warning.Level, Equals, stmtctx.WarnLevelWarning, comment) + c.Assert(warning.Err.Error(), Equals, output[i].Warning[j], comment) + } } } } diff --git a/planner/core/task.go b/planner/core/task.go index 25b642a01a7a4..88b9189417e24 100644 --- a/planner/core/task.go +++ b/planner/core/task.go @@ -16,6 +16,7 @@ package core import ( "math" + "github.com/pingcap/errors" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" @@ -1114,6 +1115,13 @@ func CheckAggCanPushCop(sctx sessionctx.Context, aggFuncs []*aggregation.AggFunc return false } if !aggregation.CheckAggPushDown(aggFunc, storeType) { + if sc.InExplainStmt { + storageName := storeType.Name() + if storeType == kv.UnSpecified { + storageName = "storage layer" + } + sc.AppendWarning(errors.New("Agg function '" + aggFunc.Name + "' can not be pushed to " + storageName)) + } return false } if !expression.CanExprsPushDown(sc, aggFunc.Args, client, storeType) { diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index 620d83d1a852f..61806044288d9 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -1487,7 +1487,7 @@ " └─Selection_13 0.83 cop[tikv] gt(test.tn.c, 50)", " └─IndexRangeScan_12 2.50 cop[tikv] table:tn, index:a(a, b, c, d) range:(1 10,1 20), keep order:false, stats:pseudo" ], - "Warning": "" + "Warning": null }, { "SQL": "select * from tn where a = 1 and b > 10 and b < 20 and c > 50 order by d limit 1", @@ -1497,7 +1497,7 @@ " └─Selection_19 0.83 cop[tikv] gt(test.tn.c, 50)", " └─IndexRangeScan_18 2.50 cop[tikv] table:tn, index:a(a, b, c, d) range:(1 10,1 20), keep order:false, stats:pseudo" ], - "Warning": "" + "Warning": null }, { "SQL": "select /*+ LIMIT_TO_COP() */ a from tn where mod(a, 2) order by a limit 1", @@ -1507,7 +1507,10 @@ " └─IndexReader_21 1.00 root index:IndexFullScan_20", " └─IndexFullScan_20 1.00 cop[tikv] table:tn, index:a(a, b, c, d) keep order:true, stats:pseudo" ], - "Warning": "[planner:1815]Optimizer Hint LIMIT_TO_COP is inapplicable" + "Warning": [ + "Scalar function 'mod'(signature: ModInt) can not be pushed to storage layer", + "[planner:1815]Optimizer Hint LIMIT_TO_COP is inapplicable" + ] }, { "SQL": "select /*+ LIMIT_TO_COP() */ a from tn where a > 10 limit 1", @@ -1517,7 +1520,7 @@ " └─Limit_11 1.00 cop[tikv] offset:0, count:1", " └─IndexRangeScan_10 1.00 cop[tikv] table:tn, index:a(a, b, c, d) range:(10,+inf], keep order:false, stats:pseudo" ], - "Warning": "" + "Warning": null } ] },