func builtinNow(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // TODO: if NOW is used in stored function or trigger, NOW will return the beginning time // of the execution. fsp := 0 sc := ctx.GetSessionVars().StmtCtx if len(args) == 1 && !args[0].IsNull() { if fsp, err = checkFsp(sc, args[0]); err != nil { d.SetNull() return d, errors.Trace(err) } } tr, err := types.RoundFrac(time.Now(), int(fsp)) if err != nil { d.SetNull() return d, errors.Trace(err) } t := types.Time{ Time: types.FromGoTime(tr), Type: mysql.TypeDatetime, // set unspecified for later round Fsp: fsp, } d.SetMysqlTime(t) return d, nil }
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract func builtinExtract(args []types.Datum, ctx context.Context) (d types.Datum, err error) { unit := args[0].GetString() vd := args[1] if vd.IsNull() { d.SetNull() return d, nil } f := types.NewFieldType(mysql.TypeDatetime) f.Decimal = types.MaxFsp val, err := vd.ConvertTo(ctx.GetSessionVars().StmtCtx, f) if err != nil { d.SetNull() return d, errors.Trace(err) } if val.IsNull() { d.SetNull() return d, nil } if val.Kind() != types.KindMysqlTime { d.SetNull() return d, errors.Errorf("need time type, but got %T", val) } t := val.GetMysqlTime() n, err1 := types.ExtractTimeNum(unit, t) if err1 != nil { d.SetNull() return d, errors.Trace(err1) } d.SetInt64(n) return d, nil }
func (af *avgFunction) updateAvg(row []types.Datum, groupKey []byte, ectx context.Context) error { ctx := af.getContext(groupKey) a := af.Args[1] value, err := a.Eval(row, ectx) if err != nil { return errors.Trace(err) } if value.IsNull() { return nil } if af.Distinct { d, err1 := ctx.DistinctChecker.Check([]interface{}{value.GetValue()}) if err1 != nil { return errors.Trace(err1) } if !d { return nil } } ctx.Value, err = types.CalculateSum(ectx.GetSessionVars().StmtCtx, ctx.Value, value) if err != nil { return errors.Trace(err) } count, err := af.Args[0].Eval(row, ectx) if err != nil { return errors.Trace(err) } ctx.Count += count.GetInt64() return nil }
func (p *physicalTableSource) addTopN(ctx context.Context, prop *requiredProperty) bool { if len(prop.props) == 0 && prop.limit != nil { p.addLimit(prop.limit) return true } if p.client == nil || !p.client.SupportRequestType(kv.ReqTypeSelect, kv.ReqSubTypeTopN) { return false } if prop.limit == nil { return false } sc := ctx.GetSessionVars().StmtCtx count := int64(prop.limit.Count + prop.limit.Offset) p.LimitCount = &count for _, prop := range prop.props { item := sortByItemToPB(sc, p.client, prop.col, prop.desc) if item == nil { // When we fail to convert any sortItem to PB struct, we should clear the environments. p.clearForTopnPushDown() return false } p.SortItemsPB = append(p.SortItemsPB, item) p.sortItems = append(p.sortItems, &ByItems{Expr: prop.col, Desc: prop.desc}) } return true }
// See http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_space func builtinSpace(args []types.Datum, ctx context.Context) (d types.Datum, err error) { x := args[0] if x.IsNull() { return d, nil } sc := ctx.GetSessionVars().StmtCtx if x.Kind() == types.KindString || x.Kind() == types.KindBytes { if _, e := types.StrToInt(sc, x.GetString()); e != nil { return d, errors.Trace(e) } } v, err := x.ToInt64(ctx.GetSessionVars().StmtCtx) if err != nil { return d, errors.Trace(err) } if v < 0 { v = 0 } if v > math.MaxInt32 { d.SetNull() } else { d.SetString(strings.Repeat(" ", int(v))) } return d, nil }
// See http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_unhex func builtinUnHex(args []types.Datum, ctx context.Context) (d types.Datum, err error) { switch args[0].Kind() { case types.KindNull: return d, nil case types.KindString: x, err := args[0].ToString() if err != nil { return d, errors.Trace(err) } bytes, err := hex.DecodeString(x) if err != nil { return d, nil } d.SetString(string(bytes)) return d, nil case types.KindInt64, types.KindUint64, types.KindMysqlHex, types.KindFloat32, types.KindFloat64, types.KindMysqlDecimal: x, _ := args[0].Cast(ctx.GetSessionVars().StmtCtx, types.NewFieldType(mysql.TypeString)) if x.IsNull() { return d, nil } bytes, err := hex.DecodeString(x.GetString()) if err != nil { return d, nil } d.SetString(string(bytes)) return d, nil default: return d, errors.Errorf("Unhex invalid args, need int or string but get %T", args[0].GetValue()) } }
// StreamUpdate implements AggregationFunction interface. func (mmf *maxMinFunction) StreamUpdate(row []types.Datum, ectx context.Context) error { ctx := mmf.getStreamedContext() if len(mmf.Args) != 1 { return errors.New("Wrong number of args for AggFuncMaxMin") } a := mmf.Args[0] value, err := a.Eval(row, ectx) if err != nil { return errors.Trace(err) } if ctx.Value.IsNull() { ctx.Value = value } if value.IsNull() { return nil } var c int c, err = ctx.Value.CompareDatum(ectx.GetSessionVars().StmtCtx, value) if err != nil { return errors.Trace(err) } if (mmf.isMax && c == -1) || (!mmf.isMax && c == 1) { ctx.Value = value } return nil }
func (af *aggFunction) streamUpdateSum(row []types.Datum, ectx context.Context) error { ctx := af.getStreamedContext() a := af.Args[0] value, err := a.Eval(row, ectx) if err != nil { return errors.Trace(err) } if value.IsNull() { return nil } if af.Distinct { d, err1 := ctx.DistinctChecker.Check([]interface{}{value.GetValue()}) if err1 != nil { return errors.Trace(err1) } if !d { return nil } } ctx.Value, err = types.CalculateSum(ectx.GetSessionVars().StmtCtx, ctx.Value, value) if err != nil { return errors.Trace(err) } ctx.Count++ return nil }
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html func builtinDatabase(args []types.Datum, ctx context.Context) (d types.Datum, err error) { currentDB := ctx.GetSessionVars().CurrentDB if currentDB == "" { return d, nil } d.SetString(currentDB) return d, nil }
func builtinConnectionID(args []types.Datum, ctx context.Context) (d types.Datum, err error) { data := ctx.GetSessionVars() if data == nil { return d, errors.Errorf("Missing session variable when evalue builtin") } d.SetUint64(data.ConnectionID) return d, nil }
func builtinUser(args []types.Datum, ctx context.Context) (d types.Datum, err error) { data := ctx.GetSessionVars() if data == nil { return d, errors.Errorf("Missing session variable when evalue builtin") } d.SetString(data.User) return d, nil }
func builtinFoundRows(arg []types.Datum, ctx context.Context) (d types.Datum, err error) { data := ctx.GetSessionVars() if data == nil { return d, errors.Errorf("Missing session variable when evalue builtin") } d.SetUint64(data.StmtCtx.FoundRows()) return d, nil }
func (e *DeleteExec) removeRow(ctx context.Context, t table.Table, h int64, data []types.Datum) error { err := t.RemoveRecord(ctx, h, data) if err != nil { return errors.Trace(err) } getDirtyDB(ctx).deleteRow(t.Meta().ID, h) ctx.GetSessionVars().StmtCtx.AddAffectedRows(1) return nil }
func getHistory(ctx context.Context) *stmtHistory { hist, ok := ctx.GetSessionVars().TxnCtx.Histroy.(*stmtHistory) if ok { return hist } hist = new(stmtHistory) ctx.GetSessionVars().TxnCtx.Histroy = hist return hist }
// Parse parses a query string to raw ast.StmtNode. func Parse(ctx context.Context, src string) ([]ast.StmtNode, error) { log.Debug("compiling", src) charset, collation := ctx.GetSessionVars().GetCharsetInfo() stmts, err := parser.New().Parse(src, charset, collation) if err != nil { log.Warnf("compiling %s, error: %v", src, err) return nil, errors.Trace(err) } return stmts, nil }
func getScanConcurrency(ctx context.Context) (int, error) { sessionVars := ctx.GetSessionVars() concurrency, err := sessionVars.GetTiDBSystemVar(variable.DistSQLScanConcurrencyVar) if err != nil { return 0, errors.Trace(err) } c, err := strconv.ParseInt(concurrency, 10, 64) log.Debugf("[%d] [DistSQL] Scan with concurrency %d", sessionVars.ConnectionID, c) return int(c), errors.Trace(err) }
// GetPrewriteValue gets binlog prewrite value in the context. func GetPrewriteValue(ctx context.Context, createIfNotExists bool) *binlog.PrewriteValue { vars := ctx.GetSessionVars() v, ok := vars.TxnCtx.Binlog.(*binlog.PrewriteValue) if !ok && createIfNotExists { schemaVer := ctx.GetSessionVars().TxnCtx.SchemaVersion v = &binlog.PrewriteValue{SchemaVersion: schemaVer} vars.TxnCtx.Binlog = v } return v }
// See http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_microsecond func builtinMicroSecond(args []types.Datum, ctx context.Context) (types.Datum, error) { d, err := convertToDuration(ctx.GetSessionVars().StmtCtx, args[0], types.MaxFsp) if err != nil || d.IsNull() { return d, errors.Trace(err) } // No need to check type here. m := int64(d.GetMysqlDuration().MicroSecond()) d.SetInt64(m) return d, nil }
func getDirtyDB(ctx context.Context) *dirtyDB { var udb *dirtyDB x := ctx.GetSessionVars().TxnCtx.DirtyDB if x == nil { udb = &dirtyDB{tables: make(map[int64]*dirtyTable)} ctx.GetSessionVars().TxnCtx.DirtyDB = udb } else { udb = x.(*dirtyDB) } return udb }
// CastValue casts a value based on column type. func CastValue(ctx context.Context, val types.Datum, col *model.ColumnInfo) (casted types.Datum, err error) { casted, err = val.ConvertTo(ctx.GetSessionVars().StmtCtx, &col.FieldType) if err != nil { if ctx.GetSessionVars().StrictSQLMode { return casted, errors.Trace(err) } // TODO: add warnings. log.Warnf("cast value error %v", err) } return casted, nil }
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_rand func builtinRand(args []types.Datum, ctx context.Context) (d types.Datum, err error) { if len(args) == 1 && !args[0].IsNull() { seed, err := args[0].ToInt64(ctx.GetSessionVars().StmtCtx) if err != nil { return d, errors.Trace(err) } rand.Seed(seed) } d.SetFloat64(rand.Float64()) return d, nil }
// GetInfoSchema gets TxnCtx InfoSchema if snapshot schema is not set, // Otherwise, snapshot schema is returned. func GetInfoSchema(ctx context.Context) infoschema.InfoSchema { sessVar := ctx.GetSessionVars() var is infoschema.InfoSchema if snap := sessVar.SnapshotInfoschema; snap != nil { is = snap.(infoschema.InfoSchema) log.Infof("[%d] use snapshot schema %d", sessVar.ConnectionID, is.SchemaMetaVersion()) } else { is = sessVar.TxnCtx.InfoSchema.(infoschema.InfoSchema) } return is }
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_curtime func builtinCurrentTime(args []types.Datum, ctx context.Context) (d types.Datum, err error) { fsp := 0 sc := ctx.GetSessionVars().StmtCtx if len(args) == 1 && !args[0].IsNull() { if fsp, err = checkFsp(sc, args[0]); err != nil { d.SetNull() return d, errors.Trace(err) } } d.SetString(time.Now().Format("15:04:05.000000")) return convertToDuration(ctx.GetSessionVars().StmtCtx, d, fsp) }
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceiling func builtinCeil(args []types.Datum, ctx context.Context) (d types.Datum, err error) { if args[0].IsNull() || args[0].Kind() == types.KindUint64 || args[0].Kind() == types.KindInt64 { return args[0], nil } f, err := args[0].ToFloat64(ctx.GetSessionVars().StmtCtx) if err != nil { return d, errors.Trace(err) } d.SetFloat64(math.Ceil(f)) return }
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_log2 func builtinLog2(args []types.Datum, ctx context.Context) (d types.Datum, err error) { sc := ctx.GetSessionVars().StmtCtx x, err := args[0].ToFloat64(sc) if err != nil { return d, errors.Trace(err) } if x <= 0 { return } d.SetFloat64(math.Log2(x)) return }
// Eval evaluates an expression to a datum. func Eval(ctx context.Context, expr ast.ExprNode) (d types.Datum, err error) { if ast.IsEvaluated(expr) { return *expr.GetDatum(), nil } e := &Evaluator{ctx: ctx, sc: ctx.GetSessionVars().StmtCtx} expr.Accept(e) if e.err != nil { return d, errors.Trace(e.err) } if ast.IsPreEvaluable(expr) && (expr.GetFlag()&ast.FlagHasFunc == 0) { expr.SetFlag(expr.GetFlag() | ast.FlagPreEvaluated) } return *expr.GetDatum(), nil }
// Add data into indices. func (t *Table) addIndices(ctx context.Context, recordID int64, r []types.Datum, bs *kv.BufferStore) (int64, error) { txn, err := ctx.GetTxn(false) if err != nil { return 0, errors.Trace(err) } // Clean up lazy check error environment defer txn.DelOption(kv.PresumeKeyNotExistsError) skipCheck := ctx.GetSessionVars().SkipConstraintCheck if t.meta.PKIsHandle && !skipCheck { // Check key exists. recordKey := t.RecordKey(recordID) e := kv.ErrKeyExists.FastGen("Duplicate entry '%d' for key 'PRIMARY'", recordID) txn.SetOption(kv.PresumeKeyNotExistsError, e) _, err = txn.Get(recordKey) if err == nil { return recordID, errors.Trace(e) } else if !terror.ErrorEqual(err, kv.ErrNotExist) { return 0, errors.Trace(err) } txn.DelOption(kv.PresumeKeyNotExistsError) } for _, v := range t.indices { if v == nil || v.Meta().State == model.StateDeleteOnly || v.Meta().State == model.StateDeleteReorganization { // if index is in delete only or delete reorganization state, we can't add it. continue } colVals, err2 := v.FetchValues(r) if err2 != nil { return 0, errors.Trace(err2) } var dupKeyErr error if !skipCheck && (v.Meta().Unique || v.Meta().Primary) { entryKey, err1 := t.genIndexKeyStr(colVals) if err1 != nil { return 0, errors.Trace(err1) } dupKeyErr = kv.ErrKeyExists.FastGen("Duplicate entry '%s' for key '%s'", entryKey, v.Meta().Name) txn.SetOption(kv.PresumeKeyNotExistsError, dupKeyErr) } if dupHandle, err := v.Create(bs, colVals, recordID); err != nil { if terror.ErrorEqual(err, kv.ErrKeyExists) { return dupHandle, errors.Trace(dupKeyErr) } return 0, errors.Trace(err) } txn.DelOption(kv.PresumeKeyNotExistsError) } return 0, nil }
// Exec implements the ast.Statement Exec interface. // This function builds an Executor from a plan. If the Executor doesn't return result, // like the INSERT, UPDATE statements, it executes in this function, if the Executor returns // result, execution is done after this function returns, in the returned ast.RecordSet Next method. func (a *statement) Exec(ctx context.Context) (ast.RecordSet, error) { b := newExecutorBuilder(ctx, a.is) e := b.build(a.plan) if b.err != nil { return nil, errors.Trace(b.err) } // ExecuteExec is not a real Executor, we only use it to build another Executor from a prepared statement. if executorExec, ok := e.(*ExecuteExec); ok { err := executorExec.Build() if err != nil { return nil, errors.Trace(err) } stmtCount(executorExec.Stmt) e = executorExec.StmtExec } // Fields or Schema are only used for statements that return result set. if len(e.Schema()) == 0 { // Check if "tidb_snapshot" is set for the write executors. // In history read mode, we can not do write operations. switch e.(type) { case *DeleteExec, *InsertExec, *UpdateExec, *ReplaceExec, *LoadData, *DDLExec: snapshotTS := ctx.GetSessionVars().SnapshotTS if snapshotTS != 0 { return nil, errors.New("can not execute write statement when 'tidb_snapshot' is set") } } defer e.Close() for { row, err := e.Next() if err != nil { return nil, errors.Trace(err) } // Even though there isn't any result set, the row is still used to indicate if there is // more work to do. // For example, the UPDATE statement updates a single row on a Next call, we keep calling Next until // There is no more rows to update. if row == nil { return nil, nil } } } return &recordSet{ executor: e, schema: e.Schema(), }, nil }
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_pow func builtinPow(args []types.Datum, ctx context.Context) (d types.Datum, err error) { sc := ctx.GetSessionVars().StmtCtx x, err := args[0].ToFloat64(sc) if err != nil { return d, errors.Trace(err) } y, err := args[1].ToFloat64(sc) if err != nil { return d, errors.Trace(err) } d.SetFloat64(math.Pow(x, y)) return d, nil }
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate func builtinLocate(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // The meaning of the elements of args. // args[0] -> SubStr // args[1] -> Str // args[2] -> Pos // eval str if args[1].IsNull() { return d, nil } str, err := args[1].ToString() if err != nil { return d, errors.Trace(err) } // eval substr if args[0].IsNull() { return d, nil } subStr, err := args[0].ToString() if err != nil { return d, errors.Trace(err) } // eval pos pos := int64(0) if len(args) == 3 { p, err := args[2].ToInt64(ctx.GetSessionVars().StmtCtx) if err != nil { return d, errors.Trace(err) } pos = p - 1 if pos < 0 || pos > int64(len(str)) { d.SetInt64(0) return d, nil } if pos > int64(len(str)-len(subStr)) { d.SetInt64(0) return d, nil } } if len(subStr) == 0 { d.SetInt64(pos + 1) return d, nil } i := strings.Index(str[pos:], subStr) if i == -1 { d.SetInt64(0) return d, nil } d.SetInt64(int64(i) + pos + 1) return d, nil }