func runStmt(ctx context.Context, s stmt.Statement, args ...interface{}) (rset.Recordset, error) { var err error var rs rset.Recordset // before every execution, we must clear affectedrows. variable.GetSessionVars(ctx).SetAffectedRows(0) switch s.(type) { case *stmts.PreparedStmt: ps := s.(*stmts.PreparedStmt) return runPreparedStmt(ctx, ps) case *stmts.ExecuteStmt: es := s.(*stmts.ExecuteStmt) rs, err = runExecute(ctx, es, args...) if err != nil { return nil, errors.Trace(err) } default: if s.IsDDL() { err = ctx.FinishTxn(false) if err != nil { return nil, errors.Trace(err) } } stmt.BindExecArgs(ctx, args) rs, err = s.Exec(ctx) stmt.ClearExecArgs(ctx) } // MySQL DDL should be auto-commit if err == nil && (s.IsDDL() || variable.ShouldAutocommit(ctx)) { err = ctx.FinishTxn(false) } return rs, errors.Trace(err) }
// Plan implements the plan.Planner interface. // The whole phase for select is // `from -> where -> lock -> group by -> having -> select fields -> distinct -> order by -> limit -> final` func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) { var ( r plan.Plan err error ) if s.From != nil { r, err = s.From.Plan(ctx) if err != nil { return nil, err } } else if s.Fields != nil { // Only evaluate fields values. fr := &rsets.SelectFromDualRset{Fields: s.Fields} r, err = fr.Plan(ctx) if err != nil { return nil, err } } // Put RowStackFromPlan here so that we can catch the origin from data after above FROM phase. r = &plans.RowStackFromPlan{Src: r} if w := s.Where; w != nil { r, err = (&rsets.WhereRset{Expr: w.Expr, Src: r}).Plan(ctx) if err != nil { return nil, err } } lock := s.Lock if variable.ShouldAutocommit(ctx) { // Locking of rows for update using SELECT FOR UPDATE only applies when autocommit // is disabled (either by beginning transaction with START TRANSACTION or by setting // autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked. // See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html lock = coldef.SelectLockNone } r = &plans.SelectLockPlan{Src: r, Lock: lock} if err := s.checkOneColumn(ctx); err != nil { return nil, errors.Trace(err) } // Get select list for futher field values evaluation. selectList, err := plans.ResolveSelectList(s.Fields, r.GetFields()) if err != nil { return nil, errors.Trace(err) } var groupBy []expression.Expression if s.GroupBy != nil { groupBy = s.GroupBy.By } if s.Having != nil { // `having` may contain aggregate functions, and we will add this to hidden fields. if err = s.Having.CheckAndUpdateSelectList(selectList, groupBy, r.GetFields()); err != nil { return nil, errors.Trace(err) } } if s.OrderBy != nil { // `order by` may contain aggregate functions, and we will add this to hidden fields. if err = s.OrderBy.CheckAndUpdateSelectList(selectList, r.GetFields()); err != nil { return nil, errors.Trace(err) } } switch { case !rsets.HasAggFields(selectList.Fields) && s.GroupBy == nil: // If no group by and no aggregate functions, we will use SelectFieldsPlan. if r, err = (&rsets.SelectFieldsRset{Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } default: if r, err = (&rsets.GroupByRset{By: groupBy, Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } } if s := s.Having; s != nil { if r, err = (&rsets.HavingRset{ Src: r, Expr: s.Expr}).Plan(ctx); err != nil { return nil, err } } if s.Distinct { r = &plans.DistinctDefaultPlan{Src: r, SelectList: selectList} } if s := s.OrderBy; s != nil { if r, err = (&rsets.OrderByRset{By: s.By, Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } } if s := s.Offset; s != nil { r = &plans.OffsetDefaultPlan{Count: s.Count, Src: r, Fields: r.GetFields()} } if s := s.Limit; s != nil { r = &plans.LimitDefaultPlan{Count: s.Count, Src: r, Fields: r.GetFields()} } r = &plans.SelectFinalPlan{Src: r, SelectList: selectList} return r, nil }