// DoPrepare prepares the statement, it can be called multiple times without // side effect. func (e *PrepareExec) DoPrepare() { vars := variable.GetSessionVars(e.Ctx) if e.ID != 0 { // Must be the case when we retry a prepare. // Make sure it is idempotent. _, ok := vars.PreparedStmts[e.ID] if ok { return } } charset, collation := variable.GetCharsetInfo(e.Ctx) stmts, err := parser.Parse(e.SQLText, charset, collation) if err != nil { e.Err = errors.Trace(err) return } if len(stmts) != 1 { e.Err = ErrPrepareMulti return } stmt := stmts[0] var extractor paramMarkerExtractor stmt.Accept(&extractor) // The parameter markers are appended in visiting order, which may not // be the same as the position order in the query string. We need to // sort it by position. sorter := ¶mMarkerSorter{markers: extractor.markers} sort.Sort(sorter) e.ParamCount = len(sorter.markers) prepared := &Prepared{ Stmt: stmt, Params: sorter.markers, SchemaVersion: e.IS.SchemaMetaVersion(), } if optimizer.IsSupported(stmt) { err := optimizer.Prepare(e.IS, e.Ctx, stmt) if err != nil { e.Err = errors.Trace(err) return } if resultSetNode, ok := stmt.(ast.ResultSetNode); ok { e.ResultFields = convertResultFields(resultSetNode.GetResultFields()) } } if e.ID == 0 { e.ID = vars.GetNextPreparedStmtID() } if e.Name != "" { vars.PreparedStmtNameToID[e.Name] = e.ID } vars.PreparedStmts[e.ID] = prepared }
// Build builds a prepared statement into an executor. func (e *ExecuteExec) Build() error { vars := variable.GetSessionVars(e.Ctx) if e.Name != "" { e.ID = vars.PreparedStmtNameToID[e.Name] } v := vars.PreparedStmts[e.ID] if v == nil { return ErrStmtNotFound } prepared := v.(*Prepared) if len(prepared.Params) != len(e.UsingVars) { return ErrWrongParamCount } for i, usingVar := range e.UsingVars { val, err := evaluator.Eval(e.Ctx, usingVar) if err != nil { return errors.Trace(err) } prepared.Params[i].SetValue(val) } if optimizer.IsSupported(prepared.Stmt) { if prepared.SchemaVersion != e.IS.SchemaMetaVersion() { // If the schema version has changed we need to prepare it again, // if this time it failed, the real reason for the error is schema changed. err := optimizer.Prepare(e.IS, e.Ctx, prepared.Stmt) if err != nil { return ErrSchemaChanged.Gen("Schema change casued error: %s", err.Error()) } prepared.SchemaVersion = e.IS.SchemaMetaVersion() } sb := &subqueryBuilder{is: e.IS} plan, err := optimizer.Optimize(e.Ctx, prepared.Stmt, sb) if err != nil { return errors.Trace(err) } b := newExecutorBuilder(e.Ctx, e.IS) stmtExec := b.build(plan) if b.err != nil { return errors.Trace(b.err) } e.StmtExec = stmtExec } else { conv := converter.Converter{} oStmt, err := conv.Convert(prepared.Stmt) if err != nil { return errors.Trace(err) } e.OldStmt = oStmt } return nil }
func checkPlan(c *C, se Session, sql, explain string) { ctx := se.(context.Context) stmts, err := Parse(ctx, sql) c.Assert(err, IsNil) stmt := stmts[0] c.Assert(optimizer.IsSupported(stmt), IsTrue) is := sessionctx.GetDomain(ctx).InfoSchema() err = optimizer.Prepare(is, ctx, stmt) c.Assert(err, IsNil) p, err := optimizer.Optimize(ctx, stmt) c.Assert(err, IsNil) planStr, err := plan.Explain(p) c.Assert(err, IsNil) c.Assert(planStr, Equals, explain) }