Skip to content

Commit 121413b

Browse files
authored
planner: do not convert update to point get if the expr has sub-query (#47454) (#50719)
close #47445
1 parent ed15ad5 commit 121413b

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

parser/ast/expressions.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ type ParamMarkerExpr interface {
960960
SetOrder(int)
961961
}
962962

963-
// ParenthesesExpr is the parentheses expression.
963+
// ParenthesesExpr is the parentheses' expression.
964964
type ParenthesesExpr struct {
965965
exprNode
966966
// Expr is the expression in parentheses.

planner/core/plan_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,22 @@ func TestTableDualAsSubQuery(t *testing.T) {
884884
tk.MustQuery("SELECT v0.c0 FROM v0 WHERE (v0.c0 IS NULL) LIKE(NULL);").Check(testkit.Rows())
885885
tk.MustQuery("SELECT v0.c0 FROM (SELECT null as c0) v0 WHERE (v0.c0 IS NULL) like (NULL);").Check(testkit.Rows())
886886
}
887+
func TestIssue47445(t *testing.T) {
888+
store, clean := testkit.CreateMockStore(t)
889+
defer clean()
890+
tk := testkit.NewTestKit(t, store)
891+
tk.MustExec("use test;")
892+
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 ))")
893+
tk.MustExec("insert into golang1 values('20230925','12023092502158016','abc','','','','')")
894+
tk.MustExec("create table golang2 (`sysgrp` varchar(20) NOT NULL,`procst` varchar(8) NOT NULL,`levlid` int(11) NOT NULL,PRIMARY key (procst));")
895+
tk.MustExec("insert into golang2 VALUES('COMMON','ACSC',90)")
896+
tk.MustExec("insert into golang2 VALUES('COMMON','abc',8)")
897+
tk.MustExec("insert into golang2 VALUES('COMMON','CH02',6)")
898+
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'")
899+
tk.MustQuery("select * from golang1").Check(testkit.Rows("20230925 12023092502158016 ACSC CI010000 ACSC EAYT 20230925"))
900+
tk.MustExec("UPDATE golang1 a SET procst= (SELECT 1 FROM golang2 c WHERE c.procst = a.procst) WHERE fcbpdt = '20230925' AND fcbpsq = '12023092502158016'")
901+
tk.MustQuery("select * from golang1").Check(testkit.Rows("20230925 12023092502158016 1 CI010000 ACSC EAYT 20230925"))
902+
}
887903

888904
// https://github.com/pingcap/tidb/issues/38310
889905
func TestNullEQConditionPlan(t *testing.T) {

planner/core/point_get_plan.go

+50-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"sort"
2020
"strconv"
2121
"strings"
22+
"sync"
2223

2324
"github.com/pingcap/errors"
2425
"github.com/pingcap/tidb/config"
@@ -1365,13 +1366,57 @@ func findInPairs(colName string, pairs []nameValuePair) int {
13651366
return -1
13661367
}
13671368

1368-
func tryUpdatePointPlan(ctx sessionctx.Context, updateStmt *ast.UpdateStmt) Plan {
1369-
// avoid using the point_get when assignment_list contains the subquery in the UPDATE.
1370-
for _, list := range updateStmt.List {
1371-
if _, ok := list.Expr.(*ast.SubqueryExpr); ok {
1372-
return nil
1369+
// Use cache to avoid allocating memory every time.
1370+
var subQueryCheckerPool = &sync.Pool{New: func() any { return &subQueryChecker{} }}
1371+
1372+
type subQueryChecker struct {
1373+
hasSubQuery bool
1374+
}
1375+
1376+
func (s *subQueryChecker) Enter(in ast.Node) (node ast.Node, skipChildren bool) {
1377+
if s.hasSubQuery {
1378+
return in, true
1379+
}
1380+
1381+
if _, ok := in.(*ast.SubqueryExpr); ok {
1382+
s.hasSubQuery = true
1383+
return in, true
1384+
}
1385+
1386+
return in, false
1387+
}
1388+
1389+
func (s *subQueryChecker) Leave(in ast.Node) (ast.Node, bool) {
1390+
// Before we enter the sub-query, we should keep visiting its children.
1391+
return in, !s.hasSubQuery
1392+
}
1393+
1394+
func isExprHasSubQuery(expr ast.Node) bool {
1395+
checker := subQueryCheckerPool.Get().(*subQueryChecker)
1396+
defer func() {
1397+
// Do not forget to reset the flag.
1398+
checker.hasSubQuery = false
1399+
subQueryCheckerPool.Put(checker)
1400+
}()
1401+
expr.Accept(checker)
1402+
return checker.hasSubQuery
1403+
}
1404+
1405+
func checkIfAssignmentListHasSubQuery(list []*ast.Assignment) bool {
1406+
for _, a := range list {
1407+
if isExprHasSubQuery(a) {
1408+
return true
13731409
}
13741410
}
1411+
return false
1412+
}
1413+
1414+
func tryUpdatePointPlan(ctx sessionctx.Context, updateStmt *ast.UpdateStmt) Plan {
1415+
// Avoid using the point_get when assignment_list contains the sub-query in the UPDATE.
1416+
if checkIfAssignmentListHasSubQuery(updateStmt.List) {
1417+
return nil
1418+
}
1419+
13751420
selStmt := &ast.SelectStmt{
13761421
Fields: &ast.FieldList{},
13771422
From: updateStmt.TableRefs,

0 commit comments

Comments
 (0)