// SetSystemVar sets system variable and updates SessionVars states. func SetSystemVar(vars *variable.SessionVars, name string, value types.Datum) error { name = strings.ToLower(name) if value.IsNull() { if name != variable.CharacterSetResults { return variable.ErrCantSetToNull } delete(vars.Systems, name) return nil } sVal, err := value.ToString() if err != nil { return errors.Trace(err) } switch name { case variable.SQLModeVar: sVal = strings.ToUpper(sVal) if strings.Contains(sVal, "STRICT_TRANS_TABLES") || strings.Contains(sVal, "STRICT_ALL_TABLES") { vars.StrictSQLMode = true } else { vars.StrictSQLMode = false } case variable.TiDBSnapshot: err = setSnapshotTS(vars, sVal) if err != nil { return errors.Trace(err) } case variable.AutocommitVar: isAutocommit := strings.EqualFold(sVal, "ON") || sVal == "1" vars.SetStatusFlag(mysql.ServerStatusAutocommit, isAutocommit) case variable.TiDBSkipConstraintCheck: vars.SkipConstraintCheck = (sVal == "1") } vars.Systems[name] = sVal return nil }
// SetSystemVar sets a system variable. func (s *SessionVars) SetSystemVar(key string, value types.Datum) error { key = strings.ToLower(key) if value.IsNull() { if key != characterSetResults { return errCantSetToNull } delete(s.systems, key) return nil } sVal, err := value.ToString() if err != nil { return errors.Trace(err) } if key == sqlMode { sVal = strings.ToUpper(sVal) if strings.Contains(sVal, "STRICT_TRANS_TABLES") || strings.Contains(sVal, "STRICT_ALL_TABLES") { s.StrictSQLMode = true } else { s.StrictSQLMode = false } } else if key == TiDBSnapshot { err = s.setSnapshotTS(sVal) if err != nil { return errors.Trace(err) } } s.systems[key] = sVal return nil }
func unflatten(datum types.Datum, tp *types.FieldType) (types.Datum, error) { if datum.IsNull() { return datum, nil } switch tp.Tp { case mysql.TypeFloat: datum.SetFloat32(float32(datum.GetFloat64())) return datum, nil case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, mysql.TypeString: return datum, nil case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: var t mysql.Time t.Type = tp.Tp t.Fsp = tp.Decimal err := t.Unmarshal(datum.GetBytes()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(t) return datum, nil case mysql.TypeDuration: dur := mysql.Duration{Duration: time.Duration(datum.GetInt64())} datum.SetValue(dur) return datum, nil case mysql.TypeNewDecimal, mysql.TypeDecimal: dec, err := mysql.ParseDecimal(datum.GetString()) if err != nil { return datum, errors.Trace(err) } if tp.Decimal >= 0 { dec = dec.Truncate(int32(tp.Decimal)) } datum.SetValue(dec) return datum, nil case mysql.TypeEnum: enum, err := mysql.ParseEnumValue(tp.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(enum) return datum, nil case mysql.TypeSet: set, err := mysql.ParseSetValue(tp.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(set) return datum, nil case mysql.TypeBit: bit := mysql.Bit{Value: datum.GetUint64(), Width: tp.Flen} datum.SetValue(bit) return datum, nil } log.Error(tp.Tp, datum) return datum, nil }
func (r *rangeBuilder) buildFormBinOp(expr *expression.ScalarFunction) []rangePoint { // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op string if v, ok := expr.Args[0].(*expression.Constant); ok { value = v.Value switch expr.FuncName.L { case ast.GE: op = ast.LE case ast.GT: op = ast.LT case ast.LT: op = ast.GT case ast.LE: op = ast.GE default: op = expr.FuncName.L } } else { value = expr.Args[1].(*expression.Constant).Value op = expr.FuncName.L } if value.IsNull() { return nil } switch op { case ast.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case ast.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case ast.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case ast.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }
func (n *finalAggregater) updateSum(val types.Datum, count uint64) error { ctx := n.getContext() if val.IsNull() { return nil } var err error ctx.Value, err = types.CalculateSum(ctx.Value, val.GetValue()) if err != nil { return errors.Trace(err) } ctx.Count += int64(count) return nil }
// Unflatten converts a raw datum to a column datum. func Unflatten(datum types.Datum, ft *types.FieldType, inIndex bool) (types.Datum, error) { if datum.IsNull() { return datum, nil } switch ft.Tp { case mysql.TypeFloat: datum.SetFloat32(float32(datum.GetFloat64())) return datum, nil case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, mysql.TypeString: return datum, nil case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: var t types.Time t.Type = ft.Tp t.Fsp = ft.Decimal var err error err = t.FromPackedUint(datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetMysqlTime(t) return datum, nil case mysql.TypeDuration: dur := types.Duration{Duration: time.Duration(datum.GetInt64())} datum.SetValue(dur) return datum, nil case mysql.TypeEnum: enum, err := types.ParseEnumValue(ft.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(enum) return datum, nil case mysql.TypeSet: set, err := types.ParseSetValue(ft.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(set) return datum, nil case mysql.TypeBit: bit := types.Bit{Value: datum.GetUint64(), Width: ft.Flen} datum.SetValue(bit) return datum, nil } return datum, nil }
func abbrDayOfMonth(arg types.Datum, ctx context.Context) (types.Datum, error) { day, err := builtinDayOfMonth([]types.Datum{arg}, ctx) if err != nil || arg.IsNull() { return types.Datum{}, errors.Trace(err) } var str string switch day.GetInt64() { case 1, 21, 31: str = "st" case 2, 22: str = "nd" case 3, 23: str = "rd" default: str = "th" } d := types.NewStringDatum(fmt.Sprintf("%d%s", day.GetInt64(), str)) return d, nil }
func (n *finalAggregater) updateMaxMin(val types.Datum, max bool) error { ctx := n.getContext() if val.IsNull() { return nil } if ctx.Value.IsNull() { ctx.Value = val return nil } c, err := ctx.Value.CompareDatum(val) if err != nil { return errors.Trace(err) } if max { if c == -1 { ctx.Value = val } } else if c == 1 { ctx.Value = val } return nil }
func updateRecord(ctx context.Context, h int64, oldData, newData []types.Datum, assignFlag []bool, t table.Table, offset int, onDuplicateUpdate bool) error { cols := t.Cols() touched := make(map[int]bool, len(cols)) assignExists := false sc := ctx.GetSessionVars().StmtCtx var newHandle types.Datum for i, hasSetExpr := range assignFlag { if !hasSetExpr { if onDuplicateUpdate { newData[i] = oldData[i] } continue } if i < offset || i >= offset+len(cols) { // The assign expression is for another table, not this. continue } colIndex := i - offset col := cols[colIndex] if col.IsPKHandleColumn(t.Meta()) { newHandle = newData[i] } if mysql.HasAutoIncrementFlag(col.Flag) { if newData[i].IsNull() { return errors.Errorf("Column '%v' cannot be null", col.Name.O) } val, err := newData[i].ToInt64(sc) if err != nil { return errors.Trace(err) } t.RebaseAutoID(val, true) } touched[colIndex] = true assignExists = true } // If no assign list for this table, no need to update. if !assignExists { return nil } // Check whether new value is valid. if err := table.CastValues(ctx, newData, cols, false); err != nil { return errors.Trace(err) } if err := table.CheckNotNull(cols, newData); err != nil { return errors.Trace(err) } // If row is not changed, we should do nothing. rowChanged := false for i := range oldData { if !touched[i] { continue } n, err := newData[i].CompareDatum(sc, oldData[i]) if err != nil { return errors.Trace(err) } if n != 0 { rowChanged = true break } } if !rowChanged { // See https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS if ctx.GetSessionVars().ClientCapability&mysql.ClientFoundRows > 0 { sc.AddAffectedRows(1) } return nil } var err error if !newHandle.IsNull() { err = t.RemoveRecord(ctx, h, oldData) if err != nil { return errors.Trace(err) } _, err = t.AddRecord(ctx, newData) } else { // Update record to new value and update index. err = t.UpdateRecord(ctx, h, oldData, newData, touched) } if err != nil { return errors.Trace(err) } dirtyDB := getDirtyDB(ctx) tid := t.Meta().ID dirtyDB.deleteRow(tid, h) dirtyDB.addRow(tid, h, newData) // Record affected rows. if !onDuplicateUpdate { sc.AddAffectedRows(1) } else { sc.AddAffectedRows(2) } return nil }
// CheckNotNull checks if nil value set to a column with NotNull flag is set. func (c *Column) CheckNotNull(data types.Datum) error { if mysql.HasNotNullFlag(c.Flag) && data.IsNull() { return errColumnCantNull.Gen("Column %s can't be null.", c.Name) } return nil }
func (er *expressionRewriter) rewriteVariable(v *ast.VariableExpr) bool { stkLen := len(er.ctxStack) name := strings.ToLower(v.Name) sessionVars := variable.GetSessionVars(er.b.ctx) globalVars := variable.GetGlobalVarAccessor(er.b.ctx) if !v.IsSystem { var d types.Datum var err error if v.Value != nil { d, err = er.ctxStack[stkLen-1].Eval(nil, er.b.ctx) if err != nil { er.err = errors.Trace(err) return false } er.ctxStack = er.ctxStack[:stkLen-1] } if !d.IsNull() { strVal, err := d.ToString() if err != nil { er.err = errors.Trace(err) return false } sessionVars.Users[name] = strings.ToLower(strVal) er.ctxStack = append(er.ctxStack, datumToConstant(d, mysql.TypeString)) } else if value, ok := sessionVars.Users[name]; ok { er.ctxStack = append(er.ctxStack, datumToConstant(types.NewStringDatum(value), mysql.TypeString)) } else { // select null user vars is permitted. er.ctxStack = append(er.ctxStack, &expression.Constant{RetType: types.NewFieldType(mysql.TypeNull)}) } return true } sysVar, ok := variable.SysVars[name] if !ok { // select null sys vars is not permitted er.err = variable.UnknownSystemVar.Gen("Unknown system variable '%s'", name) return false } if sysVar.Scope == variable.ScopeNone { er.ctxStack = append(er.ctxStack, datumToConstant(types.NewDatum(sysVar.Value), mysql.TypeString)) return true } if v.IsGlobal { value, err := globalVars.GetGlobalSysVar(er.b.ctx, name) if err != nil { er.err = errors.Trace(err) return false } er.ctxStack = append(er.ctxStack, datumToConstant(types.NewDatum(value), mysql.TypeString)) return true } d := sessionVars.GetSystemVar(name) if d.IsNull() { if sysVar.Scope&variable.ScopeGlobal == 0 { d.SetString(sysVar.Value) } else { // Get global system variable and fill it in session. globalVal, err := globalVars.GetGlobalSysVar(er.b.ctx, name) if err != nil { er.err = errors.Trace(err) return false } d.SetString(globalVal) err = sessionVars.SetSystemVar(name, d) if err != nil { er.err = errors.Trace(err) return false } } } er.ctxStack = append(er.ctxStack, datumToConstant(d, mysql.TypeString)) return true }
func (r *rangeBuilder) buildFromBinop(x *ast.BinaryOperationExpr) []rangePoint { if x.Op == opcode.OrOr { return r.union(r.build(x.L), r.build(x.R)) } else if x.Op == opcode.AndAnd { return r.intersection(r.build(x.L), r.build(x.R)) } // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op opcode.Op if _, ok := x.L.(*ast.ValueExpr); ok { value = types.NewDatum(x.L.GetValue()) switch x.Op { case opcode.GE: op = opcode.LE case opcode.GT: op = opcode.LT case opcode.LT: op = opcode.GT case opcode.LE: op = opcode.GE default: op = x.Op } } else { value = types.NewDatum(x.R.GetValue()) op = x.Op } if value.IsNull() { return nil } switch op { case opcode.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case opcode.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case opcode.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case opcode.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case opcode.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case opcode.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }
func convertDateFormat(ctx context.Context, arg types.Datum, b byte) (types.Datum, error) { var d types.Datum var err error switch b { case 'b': d, err = builtinMonthName([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(d.GetString()[:3]) } case 'M': d, err = builtinMonthName([]types.Datum{arg}, ctx) case 'm': d, err = builtinMonth([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'c': d, err = builtinMonth([]types.Datum{arg}, ctx) case 'D': d, err = abbrDayOfMonth(arg, ctx) case 'd': d, err = builtinDayOfMonth([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'e': d, err = builtinDayOfMonth([]types.Datum{arg}, ctx) case 'j': d, err = builtinDayOfYear([]types.Datum{arg}, ctx) if err == nil { d.SetString(fmt.Sprintf("%03d", d.GetInt64())) } case 'H', 'k': d, err = builtinHour([]types.Datum{arg}, ctx) if err == nil && b == 'H' && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'h', 'I', 'l': d, err = builtinHour([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { if d.GetInt64() > 12 { d.SetInt64(d.GetInt64() - 12) } else if d.GetInt64() == 0 { d.SetInt64(12) } d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'i': d, err = builtinMinute([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'p': d, err = builtinHour([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { if d.GetInt64() < 12 { d.SetString("AM") break } d.SetString("PM") } case 'r': d, err = to12Hour(arg, ctx) case 'T': d, err = builtinTime([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { duration := types.Duration{ Duration: d.GetMysqlDuration().Duration, Fsp: 0} d.SetMysqlDuration(duration) } case 'S', 's': d, err = builtinSecond([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'f': d, err = builtinMicroSecond([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%06d", d.GetInt64())) } case 'U': d, err = builtinWeek([]types.Datum{arg, types.NewIntDatum(0)}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'u': d, err = builtinWeek([]types.Datum{arg, types.NewIntDatum(1)}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'V': d, err = builtinWeek([]types.Datum{arg, types.NewIntDatum(2)}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'v': d, err = builtinWeek([]types.Datum{arg, types.NewIntDatum(3)}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%02d", d.GetInt64())) } case 'a': d, err = builtinDayName([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(d.GetString()[:3]) } case 'W': d, err = builtinDayName([]types.Datum{arg}, ctx) case 'w': d, err = builtinDayOfWeek([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetInt64(d.GetInt64() - 1) } case 'X': d, err = builtinYearWeek([]types.Datum{arg, types.NewIntDatum(2)}, ctx) if err == nil && !d.IsNull() { if d.GetInt64() == math.MaxUint32 { break } str := fmt.Sprintf("%04d", d.GetInt64()) d.SetString(fmt.Sprintf("%04s", str[:4])) } case 'x': d, err = builtinYearWeek([]types.Datum{arg, types.NewIntDatum(3)}, ctx) if err == nil && !d.IsNull() { if d.GetInt64() == math.MaxUint32 { break } str := fmt.Sprintf("%04d", d.GetInt64()) d.SetString(fmt.Sprintf("%04s", str[:4])) } case 'Y': d, err = builtinYear([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%04d", d.GetInt64())) } case 'y': d, err = builtinYear([]types.Datum{arg}, ctx) if err == nil && !d.IsNull() { str := fmt.Sprintf("%04d", d.GetInt64()) d.SetString(fmt.Sprintf("%02s", str[2:])) } default: d.SetString(string(b)) } if err == nil && !d.IsNull() { d.SetString(fmt.Sprintf("%v", d.GetValue())) } return d, errors.Trace(err) }
func (r *rangeBuilder) buildFromScalarFunc(expr *expression.ScalarFunction) []rangePoint { // TODO: It only implements the binary operation range building. And it needs to implement other scalar functions. if len(expr.Args) != 2 { return nil } if expr.FuncName.L == ast.OrOr { return r.union(r.newBuild(expr.Args[0]), r.newBuild(expr.Args[1])) } if expr.FuncName.L == ast.AndAnd { return r.intersection(r.newBuild(expr.Args[0]), r.newBuild(expr.Args[1])) } // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op string if v, ok := expr.Args[0].(*expression.Constant); ok { value = v.Value switch expr.FuncName.L { case ast.GE: op = ast.LE case ast.GT: op = ast.LT case ast.LT: op = ast.GT case ast.LE: op = ast.GE default: op = expr.FuncName.L } } else { value = expr.Args[1].(*expression.Constant).Value op = expr.FuncName.L } if value.IsNull() { return nil } switch op { case ast.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case ast.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case ast.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case ast.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }