Skip to content
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

planner: do not convert update to point get if the expr has sub-query (#47454) #47735

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions planner/core/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,3 +958,19 @@ func TestIssue40535(t *testing.T) {
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())
}

func TestIssue47445(t *testing.T) {
store, _ := testkit.CreateMockStoreAndDomain(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test;")
tk.MustExec("CREATE TABLE golang1 ( `fcbpdt` CHAR (8) COLLATE utf8_general_ci NOT NULL, `fcbpsq` VARCHAR (20) COLLATE utf8_general_ci NOT NULL, `procst` char (4) COLLATE utf8_general_ci DEFAULT NULL,`cipstx` VARCHAR (105) COLLATE utf8_general_ci DEFAULT NULL, `cipsst` CHAR (4) COLLATE utf8_general_ci DEFAULT NULL, `dyngtg` VARCHAR(4) COLLATE utf8_general_ci DEFAULT NULL, `blncdt` VARCHAR (8) COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY ( fcbpdt, fcbpsq ))")
tk.MustExec("insert into golang1 values('20230925','12023092502158016','abc','','','','')")
tk.MustExec("create table golang2 (`sysgrp` varchar(20) NOT NULL,`procst` varchar(8) NOT NULL,`levlid` int(11) NOT NULL,PRIMARY key (procst));")
tk.MustExec("insert into golang2 VALUES('COMMON','ACSC',90)")
tk.MustExec("insert into golang2 VALUES('COMMON','abc',8)")
tk.MustExec("insert into golang2 VALUES('COMMON','CH02',6)")
tk.MustExec("UPDATE golang1 a SET procst =(CASE WHEN ( SELECT levlid FROM golang2 b WHERE b.sysgrp = 'COMMON' AND b.procst = 'ACSC' ) > ( SELECT levlid FROM golang2 c WHERE c.sysgrp = 'COMMON' AND c.procst = a.procst ) THEN 'ACSC' ELSE a.procst END ), cipstx = 'CI010000', cipsst = 'ACSC', dyngtg = 'EAYT', blncdt= '20230925' WHERE fcbpdt = '20230925' AND fcbpsq = '12023092502158016'")
tk.MustQuery("select * from golang1").Check(testkit.Rows("20230925 12023092502158016 ACSC CI010000 ACSC EAYT 20230925"))
tk.MustExec("UPDATE golang1 a SET procst= (SELECT 1 FROM golang2 c WHERE c.procst = a.procst) WHERE fcbpdt = '20230925' AND fcbpsq = '12023092502158016'")
tk.MustQuery("select * from golang1").Check(testkit.Rows("20230925 12023092502158016 1 CI010000 ACSC EAYT 20230925"))
}
55 changes: 50 additions & 5 deletions planner/core/point_get_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"unsafe"

"github.com/pingcap/errors"
Expand Down Expand Up @@ -1551,13 +1552,57 @@ func findInPairs(colName string, pairs []nameValuePair) int {
return -1
}

func tryUpdatePointPlan(ctx sessionctx.Context, updateStmt *ast.UpdateStmt) Plan {
// avoid using the point_get when assignment_list contains the subquery in the UPDATE.
for _, list := range updateStmt.List {
if _, ok := list.Expr.(*ast.SubqueryExpr); ok {
return nil
// Use cache to avoid allocating memory every time.
var subQueryCheckerPool = &sync.Pool{New: func() any { return &subQueryChecker{} }}

type subQueryChecker struct {
hasSubQuery bool
}

func (s *subQueryChecker) Enter(in ast.Node) (node ast.Node, skipChildren bool) {
if s.hasSubQuery {
return in, true
}

if _, ok := in.(*ast.SubqueryExpr); ok {
s.hasSubQuery = true
return in, true
}

return in, false
}

func (s *subQueryChecker) Leave(in ast.Node) (ast.Node, bool) {
// Before we enter the sub-query, we should keep visiting its children.
return in, !s.hasSubQuery
}

func isExprHasSubQuery(expr ast.Node) bool {
checker := subQueryCheckerPool.Get().(*subQueryChecker)
defer func() {
// Do not forget to reset the flag.
checker.hasSubQuery = false
subQueryCheckerPool.Put(checker)
}()
expr.Accept(checker)
return checker.hasSubQuery
}

func checkIfAssignmentListHasSubQuery(list []*ast.Assignment) bool {
for _, a := range list {
if isExprHasSubQuery(a) {
return true
}
}
return false
}

func tryUpdatePointPlan(ctx sessionctx.Context, updateStmt *ast.UpdateStmt) Plan {
// Avoid using the point_get when assignment_list contains the sub-query in the UPDATE.
if checkIfAssignmentListHasSubQuery(updateStmt.List) {
return nil
}

selStmt := &ast.SelectStmt{
Fields: &ast.FieldList{},
From: updateStmt.TableRefs,
Expand Down