planner, session, util: support Insert hint (#16966)
This commit is contained in:
@ -894,6 +894,7 @@ const (
|
||||
ErrGeneratedColumnNonPrior = 3107
|
||||
ErrDependentByGeneratedColumn = 3108
|
||||
ErrGeneratedColumnRefAutoInc = 3109
|
||||
ErrWarnConflictingHint = 3126
|
||||
ErrInvalidJSONText = 3140
|
||||
ErrInvalidJSONPath = 3143
|
||||
ErrInvalidTypeForJSON = 3146
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;")
|
||||
|
||||
@ -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))
|
||||
|
||||
Reference in New Issue
Block a user