Skip to content

Commit

Permalink
This is an automated cherry-pick of pingcap#51319
Browse files Browse the repository at this point in the history
Signed-off-by: ti-chi-bot <[email protected]>
  • Loading branch information
hawkingrei authored and ti-chi-bot committed Feb 27, 2024
1 parent 8a48675 commit bd49c84
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 1 deletion.
44 changes: 44 additions & 0 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1388,3 +1388,47 @@ func TestDropBindBySQLDigest(t *testing.T) {
tk.MustGetErrMsg(fmt.Sprintf("drop binding for sql digest '%s'", "1"), "can't find any binding for '1'")
tk.MustGetErrMsg(fmt.Sprintf("drop binding for sql digest '%s'", ""), "sql digest is empty")
}
<<<<<<< HEAD:bindinfo/bind_test.go
=======

func TestJoinOrderHintWithBinding(t *testing.T) {
store := testkit.CreateMockStore(t)

tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t, t1, t2, t3;")
tk.MustExec("create table t(a int, b int, key(a));")
tk.MustExec("create table t1(a int, b int, key(a));")
tk.MustExec("create table t2(a int, b int, key(a));")
tk.MustExec("create table t3(a int, b int, key(a));")

tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b using select /*+ leading(t2) */ * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b")
tk.MustExec("select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b")
tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1"))
res := tk.MustQuery("show global bindings").Rows()
require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) left join `test` . `t3` on `t2` . `b` = `t3` . `b`")

tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b")
}

func TestNormalizeStmtForBinding(t *testing.T) {
tests := []struct {
sql string
normalized string
digest string
}{
{"select 1 from b where (x,y) in ((1, 3), ('3', 1))", "select ? from `b` where row ( `x` , `y` ) in ( ... )", "ab6c607d118c24030807f8d1c7c846ec23e3b752fd88ed763bb8e26fbfa56a83"},
{"select 1 from b where (x,y) in ((1, 3), ('3', 1), (2, 3))", "select ? from `b` where row ( `x` , `y` ) in ( ... )", "ab6c607d118c24030807f8d1c7c846ec23e3b752fd88ed763bb8e26fbfa56a83"},
{"select 1 from b where (x,y) in ((1, 3), ('3', 1), (2, 3),('x', 'y'))", "select ? from `b` where row ( `x` , `y` ) in ( ... )", "ab6c607d118c24030807f8d1c7c846ec23e3b752fd88ed763bb8e26fbfa56a83"},
{"select 1 from b where (x,y) in ((1, 3), ('3', 1), (2, 3),('x', 'y'),('x', 'y'))", "select ? from `b` where row ( `x` , `y` ) in ( ... )", "ab6c607d118c24030807f8d1c7c846ec23e3b752fd88ed763bb8e26fbfa56a83"},
{"select 1 from b where (x) in ((1), ('3'), (2),('x'),('x'))", "select ? from `b` where ( `x` ) in ( ( ... ) )", "03e6e1eb3d76b69363922ff269284b359ca73351001ba0e82d3221c740a6a14c"},
{"select 1 from b where (x) in ((1), ('3'), (2),('x'))", "select ? from `b` where ( `x` ) in ( ( ... ) )", "03e6e1eb3d76b69363922ff269284b359ca73351001ba0e82d3221c740a6a14c"},
}
for _, test := range tests {
stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, test.sql)
n, digest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true))
require.Equal(t, test.normalized, n)
require.Equal(t, test.digest, digest)
}
}
>>>>>>> 707b0a4e381 (parser: support (Row(..),Row(..))=>(..) in the binding mode (#51319)):pkg/bindinfo/tests/bind_test.go
165 changes: 164 additions & 1 deletion parser/digester.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,20 @@ func (d *sqlDigester) normalize(sql string, keepHint bool) {
continue
}

<<<<<<< HEAD:parser/digester.go
d.reduceLit(&currTok)
=======
d.reduceLit(&currTok, forBinding)
if forPlanReplayerReload {
// Apply for plan replayer to match specific rules, changing IN (...) to IN (?). This can avoid plan replayer load failures caused by parse errors.
d.replaceSingleLiteralWithInList(&currTok)
} else if forBinding {
// Apply binding matching specific rules, IN (?) => IN ( ... ) #44298
d.reduceInListWithSingleLiteral(&currTok)
// In (Row(...)) => In (...) #51222
d.reduceInRowListWithSingleLiteral(&currTok)
}
>>>>>>> 707b0a4e381 (parser: support (Row(..),Row(..))=>(..) in the binding mode (#51319)):pkg/parser/digester.go

if currTok.tok == identifier {
if strings.HasPrefix(currTok.lit, "_") {
Expand Down Expand Up @@ -267,7 +280,7 @@ func (d *sqlDigester) reduceOptimizerHint(tok *token) (reduced bool) {
return
}

func (d *sqlDigester) reduceLit(currTok *token) {
func (d *sqlDigester) reduceLit(currTok *token, forBinding bool) {
if !d.isLit(*currTok) {
return
}
Expand All @@ -294,6 +307,29 @@ func (d *sqlDigester) reduceLit(currTok *token) {
return
}

<<<<<<< HEAD:parser/digester.go
=======
// Aggressive reduce lists.
last4 := d.tokens.back(4)
if d.isGenericLists(last4) {
d.tokens.popBack(4)
currTok.tok = genericSymbolList
currTok.lit = "..."
return
}
// reduce "In (row(...), row(...))" to "In (row(...))"
// final, it will be reduced to "In (...)". Issue: #51222
if forBinding {
last9 := d.tokens.back(9)
if d.isGenericRowListsWithIn(last9) {
d.tokens.popBack(5)
currTok.tok = genericSymbolList
currTok.lit = "..."
return
}
}

>>>>>>> 707b0a4e381 (parser: support (Row(..),Row(..))=>(..) in the binding mode (#51319)):pkg/parser/digester.go
// order by n => order by n
if currTok.tok == intLit {
if d.isOrderOrGroupBy() {
Expand All @@ -306,6 +342,110 @@ func (d *sqlDigester) reduceLit(currTok *token) {
currTok.lit = "?"
}

<<<<<<< HEAD:parser/digester.go
=======
func (d *sqlDigester) isGenericLists(last4 []token) bool {
if len(last4) < 4 {
return false
}
if !(last4[0].tok == genericSymbol || last4[0].tok == genericSymbolList) {
return false
}
if last4[1].lit != ")" {
return false
}
if !d.isComma(last4[2]) {
return false
}
if last4[3].lit != "(" {
return false
}
return true
}

// In (Row(...), Row(...)) => In (Row(...))
func (d *sqlDigester) isGenericRowListsWithIn(last9 []token) bool {
if len(last9) < 7 {
return false
}
if !d.isInKeyword(last9[0]) {
return false
}
if last9[1].lit != "(" {
return false
}
if !d.isRowKeyword(last9[2]) {
return false
}
if last9[3].lit != "(" {
return false
}
if !(last9[4].tok == genericSymbol || last9[4].tok == genericSymbolList) {
return false
}
if last9[5].lit != ")" {
return false
}
if !d.isComma(last9[6]) {
return false
}
if !d.isRowKeyword(last9[7]) {
return false
}
if last9[8].lit != "(" {
return false
}
return true
}

// IN (...) => IN (?) Issue: #43192
func (d *sqlDigester) replaceSingleLiteralWithInList(currTok *token) {
last5 := d.tokens.back(5)
if len(last5) == 5 &&
d.isInKeyword(last5[0]) &&
d.isLeftParen(last5[1]) &&
last5[2].lit == "." &&
last5[3].lit == "." &&
last5[4].lit == "." &&
d.isRightParen(*currTok) {
d.tokens.popBack(3)
d.tokens.pushBack(token{genericSymbol, "?"})
return
}
}

// IN (?) => IN (...) Issue: #44298
func (d *sqlDigester) reduceInListWithSingleLiteral(currTok *token) {
last3 := d.tokens.back(3)
if len(last3) == 3 &&
d.isInKeyword(last3[0]) &&
d.isLeftParen(last3[1]) &&
last3[2].tok == genericSymbol &&
d.isRightParen(*currTok) {
d.tokens.popBack(1)
d.tokens.pushBack(token{genericSymbolList, "..."})
return
}
}

// In (Row(...)) => In (...) #51222
func (d *sqlDigester) reduceInRowListWithSingleLiteral(currTok *token) {
last5 := d.tokens.back(6)
if len(last5) == 6 &&
d.isInKeyword(last5[0]) &&
d.isLeftParen(last5[1]) &&
d.isRowKeyword(last5[2]) &&
d.isLeftParen(last5[3]) &&
(last5[4].tok == genericSymbolList || last5[4].tok == genericSymbol) &&
d.isRightParen(last5[5]) &&
d.isRightParen(*currTok) {
d.tokens.popBack(4)
d.tokens.pushBack(token{genericSymbolList, "..."})
return
}
}

>>>>>>> 707b0a4e381 (parser: support (Row(..),Row(..))=>(..) in the binding mode (#51319)):pkg/parser/digester.go
func (d *sqlDigester) isPrefixByUnary(currTok int) (isUnary bool) {
if !d.isNumLit(currTok) {
return
Expand Down Expand Up @@ -414,6 +554,29 @@ func (*sqlDigester) isComma(tok token) (isComma bool) {
return
}

<<<<<<< HEAD:parser/digester.go
=======
func (*sqlDigester) isLeftParen(tok token) (isLeftParen bool) {
isLeftParen = tok.lit == "("
return
}

func (*sqlDigester) isRightParen(tok token) (isLeftParen bool) {
isLeftParen = tok.lit == ")"
return
}

func (*sqlDigester) isInKeyword(tok token) (isInKeyword bool) {
isInKeyword = tok.lit == "in"
return
}

func (*sqlDigester) isRowKeyword(tok token) (isRowKeyword bool) {
isRowKeyword = tok.lit == "row"
return
}

>>>>>>> 707b0a4e381 (parser: support (Row(..),Row(..))=>(..) in the binding mode (#51319)):pkg/parser/digester.go
type token struct {
tok int
lit string
Expand Down
29 changes: 29 additions & 0 deletions pkg/bindinfo/tests/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
load("@io_bazel_rules_go//go:def.bzl", "go_test")

go_test(
name = "tests_test",
timeout = "short",
srcs = [
"bind_test.go",
"main_test.go",
],
flaky = True,
race = "on",
shard_count = 16,
deps = [
"//pkg/bindinfo",
"//pkg/bindinfo/internal",
"//pkg/bindinfo/norm",
"//pkg/domain",
"//pkg/parser",
"//pkg/parser/model",
"//pkg/parser/terror",
"//pkg/testkit",
"//pkg/testkit/testsetup",
"//pkg/util",
"//pkg/util/parser",
"//pkg/util/stmtsummary",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
],
)

0 comments on commit bd49c84

Please sign in to comment.