Skip to content

Commit

Permalink
planner: fix the issue that bindings with query-level hint can not ta…
Browse files Browse the repository at this point in the history
…ke effect for replace statements (#54048) (#54092)

close #53834
  • Loading branch information
ti-chi-bot authored Jun 18, 2024
1 parent 9184366 commit ff2feb6
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 9 deletions.
24 changes: 24 additions & 0 deletions bindinfo/session_handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package bindinfo_test

import (
"context"
"fmt"
"strconv"
"testing"
"time"
Expand Down Expand Up @@ -459,6 +460,29 @@ func TestDropSingleBindings(t *testing.T) {
require.Len(t, rows, 0)
}

func TestIssue53834(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec(`create table t (a varchar(1024))`)
tk.MustExec(`insert into t values (space(1024))`)
for i := 0; i < 12; i++ {
tk.MustExec(`insert into t select * from t`)
}
oomAction := tk.MustQuery(`select @@tidb_mem_oom_action`).Rows()[0][0].(string)
defer func() {
tk.MustExec(fmt.Sprintf(`set global tidb_mem_oom_action='%v'`, oomAction))
}()

tk.MustExec(`set global tidb_mem_oom_action='cancel'`)
err := tk.ExecToErr(`replace into t select /*+ memory_quota(1 mb) */ * from t`)
require.ErrorContains(t, err, "cancelled due to exceeding the allowed memory limit")

tk.MustExec(`create binding for replace into t select * from t using replace into t select /*+ memory_quota(1 mb) */ * from t`)
err = tk.ExecToErr(`replace into t select * from t`)
require.ErrorContains(t, err, "cancelled due to exceeding the allowed memory limit")
}

func TestPreparedStmt(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand Down
2 changes: 1 addition & 1 deletion planner/optimize.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func Optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is in
}
metrics.BindUsageCounter.WithLabelValues(scope).Inc()
hint.BindHint(stmtNode, binding.Hint)
curStmtHints, _, curWarns := handleStmtHints(binding.Hint.GetFirstTableHints())
curStmtHints, _, curWarns := handleStmtHints(binding.Hint.GetStmtHints())
sessVars.StmtCtx.StmtHints = curStmtHints
// update session var by hint /set_var/
for name, val := range sessVars.StmtCtx.StmtHints.SetVars {
Expand Down
6 changes: 3 additions & 3 deletions session/session_test/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2185,11 +2185,11 @@ func TestStmtHints(t *testing.T) {
val = int64(1) * 1024 * 1024
require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val))

tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 select /*+ MEMORY_QUOTA(3 MB) */ * from t1;")
tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 select /*+ MEMORY_QUOTA(1 MB) */ * from t1;")
val = int64(1) * 1024 * 1024
require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val))
require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1)
require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "[util:3126]Hint MEMORY_QUOTA(`3145728`) is ignored as conflicting/duplicated.")
require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 2)
require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "[util:3126]Hint MEMORY_QUOTA(`1048576`) is ignored as conflicting/duplicated.")

// Test NO_INDEX_MERGE hint
tk.Session().GetSessionVars().SetEnableIndexMerge(true)
Expand Down
39 changes: 34 additions & 5 deletions util/hint/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,20 @@ type HintsSet struct {
indexHints [][]*ast.IndexHint // Slice offset is the traversal order of `TableName` in the ast.
}

// GetFirstTableHints gets the first table hints.
func (hs *HintsSet) GetFirstTableHints() []*ast.TableOptimizerHint {
// GetStmtHints gets all statement-level hints.
func (hs *HintsSet) GetStmtHints() []*ast.TableOptimizerHint {
var result []*ast.TableOptimizerHint
if len(hs.tableHints) > 0 {
return hs.tableHints[0]
result = append(result, hs.tableHints[0]...) // keep the same behavior with prior implementation
}
return nil
for _, tHints := range hs.tableHints[1:] {
for _, h := range tHints {
if isStmtHint(h) {
result = append(result, h)
}
}
}
return result
}

// ContainTableHint checks whether the table hint set contains a hint.
Expand Down Expand Up @@ -88,7 +96,18 @@ func ExtractTableHintsFromStmtNode(node ast.Node, sctx sessionctx.Context) []*as
case *ast.InsertStmt:
// check duplicated hints
checkInsertStmtHintDuplicated(node, sctx)
return x.TableHints
result := make([]*ast.TableOptimizerHint, 0, len(x.TableHints))
result = append(result, x.TableHints...)
if x.Select != nil {
// support statement-level hint in sub-select: "insert into t select /* ... */ ..."
// TODO: support this for Update and Delete as well
for _, h := range ExtractTableHintsFromStmtNode(x.Select, sctx) {
if isStmtHint(h) {
result = append(result, h)
}
}
}
return result
case *ast.ExplainStmt:
return ExtractTableHintsFromStmtNode(x.Stmt, sctx)
case *ast.SetOprStmt:
Expand Down Expand Up @@ -634,3 +653,13 @@ func GenerateQBName(nodeType NodeType, blockOffset int) (model.CIStr, error) {
}
return model.NewCIStr(fmt.Sprintf("%s%d", defaultSelectBlockPrefix, blockOffset)), nil
}

// isStmtHint checks whether this hint is a statement-level hint.
func isStmtHint(h *ast.TableOptimizerHint) bool {
switch h.HintName.L {
case "max_execution_time", "memory_quota", "resource_group":
return true
default:
return false
}
}

0 comments on commit ff2feb6

Please sign in to comment.