From 8c4319cc6c79a871db831a718d986ca19052a72a Mon Sep 17 00:00:00 2001 From: Jk Xu <54522439+Dousir9@users.noreply.github.com> Date: Tue, 7 Feb 2023 18:45:58 -0600 Subject: [PATCH] This is an automated cherry-pick of #40593 Signed-off-by: ti-chi-bot --- planner/core/plan_test.go | 70 +++++++++++++++++++++++++++++ planner/core/rule_topn_push_down.go | 14 ++++++ 2 files changed, 84 insertions(+) diff --git a/planner/core/plan_test.go b/planner/core/plan_test.go index c08099eb89468..e12e2221b9fbe 100644 --- a/planner/core/plan_test.go +++ b/planner/core/plan_test.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/expression/aggregation" + "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/parser/ast" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" @@ -885,3 +886,72 @@ func TestNullEQConditionPlan(t *testing.T) { {"Point_Get_5", "root", "handle:0"}, }) } +<<<<<<< HEAD +======= + +// https://github.com/pingcap/tidb/issues/38304 +// https://github.com/pingcap/tidb/issues/38654 +func TestOuterJoinOnNull(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t0(c0 BLOB(5), c1 BLOB(5));") + tk.MustExec("CREATE TABLE t1 (c0 BOOL);") + tk.MustExec("INSERT INTO t1 VALUES(false);") + tk.MustExec("INSERT INTO t0(c0, c1) VALUES ('>', true);") + tk.MustQuery("SELECT * FROM t0 LEFT OUTER JOIN t1 ON NULL; ").Check(testkit.Rows("> 1 ")) + tk.MustQuery("SELECT NOT '2' =(t1.c0 AND t0.c1 IS NULL) FROM t0 LEFT OUTER JOIN t1 ON NULL; ").Check(testkit.Rows("1")) + tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE NOT '2' =(t1.c0 AND t0.c1 IS NULL); ").Check(testkit.Rows("> 1 ")) + tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE t1.c0 or true; ").Check(testkit.Rows("> 1 ")) + tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE not(t1.c0 and false); ").Check(testkit.Rows("> 1 ")) + + tk.MustExec("CREATE TABLE t2(c0 INT);") + tk.MustExec("CREATE TABLE t3(c0 INT);") + tk.MustExec("INSERT INTO t3 VALUES (1);") + tk.MustQuery("SELECT ((NOT ('i'))AND(t2.c0)) IS NULL FROM t2 RIGHT JOIN t3 ON t3.c0;").Check(testkit.Rows("1")) + tk.MustQuery("SELECT * FROM t2 RIGHT JOIN t3 ON t2.c0 WHERE ((NOT ('i'))AND(t2.c0)) IS NULL;").Check(testkit.Rows(" 1")) +} + +func TestJSONPlanInExplain(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(id int, key(id))") + tk.MustExec("create table t2(id int, key(id))") + + var input []string + var output []struct { + SQL string + JSONPlan []*core.ExplainInfoForEncode + } + planSuiteData := core.GetJSONPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, test := range input { + resJSON := tk.MustQuery(test).Rows() + var res []*core.ExplainInfoForEncode + require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), &res)) + for j, expect := range output[i].JSONPlan { + require.Equal(t, expect.ID, res[j].ID) + require.Equal(t, expect.EstRows, res[j].EstRows) + require.Equal(t, expect.ActRows, res[j].ActRows) + require.Equal(t, expect.TaskType, res[j].TaskType) + require.Equal(t, expect.AccessObject, res[j].AccessObject) + require.Equal(t, expect.OperatorInfo, res[j].OperatorInfo) + } + } +} + +func TestIssue40535(t *testing.T) { + store := testkit.CreateMockStore(t) + var cfg kv.InjectionConfig + tk := testkit.NewTestKit(t, kv.NewInjectedStore(store, &cfg)) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1; drop table if exists t2;") + tk.MustExec("CREATE TABLE `t1`(`c1` bigint(20) NOT NULL DEFAULT '-2312745469307452950', `c2` datetime DEFAULT '5316-02-03 06:54:49', `c3` tinyblob DEFAULT NULL, PRIMARY KEY (`c1`) /*T![clustered_index] CLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;") + tk.MustExec("CREATE TABLE `t2`(`c1` set('kn8pu','7et','vekx6','v3','liwrh','q14','1met','nnd5i','5o0','8cz','l') DEFAULT '7et,vekx6,liwrh,q14,1met', `c2` float DEFAULT '1.683167', KEY `k1` (`c2`,`c1`), KEY `k2` (`c2`)) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_chinese_ci;") + tk.MustExec("(select /*+ agg_to_cop()*/ locate(t1.c3, t1.c3) as r0, t1.c3 as r1 from t1 where not( IsNull(t1.c1)) order by r0,r1) union all (select concat_ws(',', t2.c2, t2.c1) as r0, t2.c1 as r1 from t2 order by r0, r1) order by 1 limit 273;") + require.Empty(t, tk.Session().LastMessage()) +} +>>>>>>> 2aad38831e0 (planner: when pushing `topN` down below `proj`, check whether `topN.ByItems` contains a column(with ID=0) generated by `proj` (#40593)) diff --git a/planner/core/rule_topn_push_down.go b/planner/core/rule_topn_push_down.go index 63da676655198..6e1fceb8f5127 100644 --- a/planner/core/rule_topn_push_down.go +++ b/planner/core/rule_topn_push_down.go @@ -136,6 +136,20 @@ func (p *LogicalProjection) pushDownTopN(topN *LogicalTopN, opt *logicalOptimize topN.ByItems = append(topN.ByItems[:i], topN.ByItems[i+1:]...) } } + + // if topN.ByItems contains a column(with ID=0) generated by projection, projection will prevent the optimizer from pushing topN down. + for _, by := range topN.ByItems { + cols := expression.ExtractColumns(by.Expr) + for _, col := range cols { + if col.ID == 0 && p.Schema().Contains(col) { + // check whether the column is generated by projection + if !p.children[0].Schema().Contains(col) { + p.children[0] = p.children[0].pushDownTopN(nil, opt) + return topN.setChild(p, opt) + } + } + } + } } p.children[0] = p.children[0].pushDownTopN(topN, opt) return p