// 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(), } err = plan.PrepareStmt(e.IS, e.Ctx, stmt) if err != nil { e.Err = errors.Trace(err) return } if resultSetNode, ok := stmt.(ast.ResultSetNode); ok { e.ResultFields = 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].SetDatum(val) } ast.ResetEvaluatedFlag(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 := plan.PrepareStmt(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} p, err := plan.Optimize(e.Ctx, prepared.Stmt, sb, e.IS) if err != nil { return errors.Trace(err) } b := newExecutorBuilder(e.Ctx, e.IS) stmtExec := b.build(p) if b.err != nil { return errors.Trace(b.err) } e.StmtExec = stmtExec e.Stmt = prepared.Stmt return nil }