planner, session, util: support Insert hint (#16966)

This commit is contained in:
Song Gao
2020-06-17 15:56:01 +08:00
committed by GitHub
parent 463d085342
commit b59cb1a94d
6 changed files with 63 additions and 5 deletions

View File

@ -894,6 +894,7 @@ const (
ErrGeneratedColumnNonPrior = 3107
ErrDependentByGeneratedColumn = 3108
ErrGeneratedColumnRefAutoInc = 3109
ErrWarnConflictingHint = 3126
ErrInvalidJSONText = 3140
ErrInvalidJSONPath = 3143
ErrInvalidTypeForJSON = 3146

View File

@ -887,6 +887,7 @@ var MySQLErrName = map[uint16]string{
ErrGeneratedColumnNonPrior: "Generated column can refer only to generated columns defined prior to it.",
ErrDependentByGeneratedColumn: "Column '%s' has a generated column dependency.",
ErrGeneratedColumnRefAutoInc: "Generated column '%s' cannot refer to auto-increment column.",
ErrWarnConflictingHint: "Hint %s is ignored as conflicting/duplicated.",
ErrInvalidFieldSize: "Invalid size for column '%s'.",
ErrIncorrectType: "Incorrect type for argument %s in function %s.",
ErrInvalidJSONData: "Invalid JSON data provided to function %s: %s",

View File

@ -833,7 +833,7 @@ func (e *Explain) RenderResult() error {
}
case ast.ExplainFormatHint:
hints := GenHintsFromPhysicalPlan(e.TargetPlan)
hints = append(hints, hint.ExtractTableHintsFromStmtNode(e.ExecStmt)...)
hints = append(hints, hint.ExtractTableHintsFromStmtNode(e.ExecStmt, nil)...)
e.Rows = append(e.Rows, []string{hint.RestoreOptimizerHints(hints)})
default:
return errors.Errorf("explain format '%s' is not supported now", e.Format)

View File

@ -52,7 +52,7 @@ func Optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is in
sctx.PrepareTSFuture(ctx)
tableHints := hint.ExtractTableHintsFromStmtNode(node)
tableHints := hint.ExtractTableHintsFromStmtNode(node, sctx)
stmtHints, warns := handleStmtHints(tableHints)
defer func() {
sctx.GetSessionVars().StmtCtx.StmtHints = stmtHints

View File

@ -3096,6 +3096,18 @@ func (s *testSessionSuite2) TestStmtHints(c *C) {
c.Assert(tk.Se.GetSessionVars().StmtCtx.MemTracker.CheckBytesLimit(val), IsTrue)
c.Assert(tk.Se.GetSessionVars().StmtCtx.GetWarnings()[0].Err.Error(), Equals, "Setting the MEMORY_QUOTA to 0 means no memory limit")
tk.MustExec("use test")
tk.MustExec("create table t1(a int);")
tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 (a) values (1);")
val = int64(1) * 1024 * 1024
c.Assert(tk.Se.GetSessionVars().StmtCtx.MemTracker.CheckBytesLimit(val), IsTrue)
tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 select /*+ MEMORY_QUOTA(3 MB) */ * from t1;")
val = int64(1) * 1024 * 1024
c.Assert(tk.Se.GetSessionVars().StmtCtx.MemTracker.CheckBytesLimit(val), IsTrue)
c.Assert(tk.Se.GetSessionVars().StmtCtx.GetWarnings(), HasLen, 1)
c.Assert(tk.Se.GetSessionVars().StmtCtx.GetWarnings()[0].Err.Error(), Equals, "[util:3126]Hint MEMORY_QUOTA(`3145728`) is ignored as conflicting/duplicated.")
// Test NO_INDEX_MERGE hint
tk.Se.GetSessionVars().SetEnableIndexMerge(true)
tk.MustExec("select /*+ NO_INDEX_MERGE() */ 1;")

View File

@ -23,11 +23,19 @@ import (
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/format"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/errno"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/util/logutil"
"go.uber.org/zap"
)
var supportedHintNameForInsertStmt = map[string]struct{}{}
func init() {
supportedHintNameForInsertStmt["memory_quota"] = struct{}{}
}
// HintsSet contains all hints of a query.
type HintsSet struct {
tableHints [][]*ast.TableOptimizerHint // Slice offset is the traversal order of `SelectStmt` in the ast.
@ -55,7 +63,7 @@ func (hs *HintsSet) ContainTableHint(hint string) bool {
}
// ExtractTableHintsFromStmtNode extracts table hints from this node.
func ExtractTableHintsFromStmtNode(node ast.Node) []*ast.TableOptimizerHint {
func ExtractTableHintsFromStmtNode(node ast.Node, sctx sessionctx.Context) []*ast.TableOptimizerHint {
switch x := node.(type) {
case *ast.SelectStmt:
return x.TableHints
@ -63,14 +71,50 @@ func ExtractTableHintsFromStmtNode(node ast.Node) []*ast.TableOptimizerHint {
return x.TableHints
case *ast.DeleteStmt:
return x.TableHints
// TODO: support hint for InsertStmt
case *ast.InsertStmt:
//check duplicated hints
checkInsertStmtHintDuplicated(node, sctx)
return x.TableHints
case *ast.ExplainStmt:
return ExtractTableHintsFromStmtNode(x.Stmt)
return ExtractTableHintsFromStmtNode(x.Stmt, sctx)
default:
return nil
}
}
// checkInsertStmtHintDuplicated check whether existed the duplicated hints in both insertStmt and its selectStmt.
// If existed, it would send a warning message.
func checkInsertStmtHintDuplicated(node ast.Node, sctx sessionctx.Context) {
switch x := node.(type) {
case *ast.InsertStmt:
if len(x.TableHints) > 0 {
var supportedHint *ast.TableOptimizerHint
for _, hint := range x.TableHints {
if _, ok := supportedHintNameForInsertStmt[hint.HintName.L]; ok {
supportedHint = hint
break
}
}
if supportedHint != nil {
var duplicatedHint *ast.TableOptimizerHint
for _, hint := range ExtractTableHintsFromStmtNode(x.Select, nil) {
if hint.HintName.L == supportedHint.HintName.L {
duplicatedHint = hint
break
}
}
if duplicatedHint != nil {
hint := fmt.Sprintf("%s(`%v`)", duplicatedHint.HintName.O, duplicatedHint.HintData)
err := terror.ClassUtil.New(errno.ErrWarnConflictingHint, fmt.Sprintf(errno.MySQLErrName[errno.ErrWarnConflictingHint], hint))
sctx.GetSessionVars().StmtCtx.AppendWarning(err)
}
}
}
default:
return
}
}
// RestoreOptimizerHints restores these hints.
func RestoreOptimizerHints(hints []*ast.TableOptimizerHint) string {
hintsStr := make([]string, 0, len(hints))