// Eval implements the Expression Eval interface. func (da *DateAdd) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) { dv, err := da.Date.Eval(ctx, args) if dv == nil || err != nil { return nil, errors.Trace(err) } sv, err := types.ToString(dv) if err != nil { return nil, errors.Trace(err) } f := types.NewFieldType(mysql.TypeDatetime) f.Decimal = mysql.MaxFsp dv, err = types.Convert(sv, f) if dv == nil || err != nil { return nil, errors.Trace(err) } t, ok := dv.(mysql.Time) if !ok { return nil, errors.Errorf("need time type, but got %T", dv) } iv, err := da.Interval.Eval(ctx, args) if iv == nil || err != nil { return nil, errors.Trace(err) } format, err := types.ToString(iv) if err != nil { return nil, errors.Trace(err) } years, months, days, durations, err := mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(format)) if err != nil { return nil, errors.Trace(err) } t.Time = t.Time.Add(durations) t.Time = t.Time.AddDate(int(years), int(months), int(days)) // "2011-11-11 10:10:20.000000" outputs "2011-11-11 10:10:20". if t.Time.Nanosecond() == 0 { t.Fsp = 0 } return t, nil }
func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interface{}) ( *evalArgsResult, error) { ret := &evalArgsResult{time: mysql.ZeroTimestamp} dVal, err := da.Date.Eval(ctx, args) if dVal == nil || err != nil { return ret, errors.Trace(err) } dValStr, err := types.ToString(dVal) if err != nil { return ret, errors.Trace(err) } f := types.NewFieldType(mysql.TypeDatetime) f.Decimal = mysql.MaxFsp dVal, err = types.Convert(dValStr, f) if dVal == nil || err != nil { return ret, errors.Trace(err) } var ok bool ret.time, ok = dVal.(mysql.Time) if !ok { return ret, errors.Errorf("need time type, but got %T", dVal) } iVal, err := da.Interval.Eval(ctx, args) if iVal == nil || err != nil { ret.time = mysql.ZeroTimestamp return ret, errors.Trace(err) } // handle adddate(expr,days) or subdate(expr,days) form if da.Form == DateArithDaysForm { if iVal, err = da.evalDaysForm(iVal); err != nil { return ret, errors.Trace(err) } } iValStr, err := types.ToString(iVal) if err != nil { return ret, errors.Trace(err) } ret.year, ret.month, ret.day, ret.duration, err = mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(iValStr)) if err != nil { return ret, errors.Trace(err) } return ret, nil }
func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interface{}) ( mysql.Time, int64, int64, int64, time.Duration, error) { dVal, err := da.Date.Eval(ctx, args) if dVal == nil || err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } dValStr, err := types.ToString(dVal) if err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } f := types.NewFieldType(mysql.TypeDatetime) f.Decimal = mysql.MaxFsp dVal, err = types.Convert(dValStr, f) if dVal == nil || err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } t, ok := dVal.(mysql.Time) if !ok { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Errorf("need time type, but got %T", dVal) } iVal, err := da.Interval.Eval(ctx, args) if iVal == nil || err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } iValStr, err := types.ToString(iVal) if err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } years, months, days, durations, err := mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(iValStr)) if err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } return t, years, months, days, durations, nil }
func (e *Evaluator) funcDateArith(v *ast.FuncDateArithExpr) bool { // health check for date and interval nodeDate := v.Date.GetValue() if types.IsNil(nodeDate) { v.SetValue(nil) return true } nodeInterval := v.Interval.GetValue() if types.IsNil(nodeInterval) { v.SetValue(nil) return true } // parse date fieldType := mysql.TypeDate var resultField *types.FieldType switch x := nodeDate.(type) { case mysql.Time: if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } case string: if !mysql.IsDateFormat(x) { fieldType = mysql.TypeDatetime } case int64: if t, err := mysql.ParseTimeFromInt64(x); err == nil { if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } } } if mysql.IsClockUnit(v.Unit) { fieldType = mysql.TypeDatetime } resultField = types.NewFieldType(fieldType) resultField.Decimal = mysql.MaxFsp value, err := types.Convert(nodeDate, resultField) if err != nil { e.err = ErrInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate) return false } if types.IsNil(value) { e.err = ErrInvalidOperation.Gen("DateArith invalid args, need date but get %v", value) return false } result, ok := value.(mysql.Time) if !ok { e.err = ErrInvalidOperation.Gen("DateArith need time type, but got %T", value) return false } // parse interval var interval string if strings.ToLower(v.Unit) == "day" { day, err2 := parseDayInterval(nodeInterval) if err2 != nil { e.err = ErrInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeInterval) return false } interval = fmt.Sprintf("%d", day) } else { interval = fmt.Sprintf("%v", nodeInterval) } year, month, day, duration, err := mysql.ExtractTimeValue(v.Unit, interval) if err != nil { e.err = errors.Trace(err) return false } if v.Op == ast.DateSub { year, month, day, duration = -year, -month, -day, -duration } result.Time = result.Time.Add(duration) result.Time = result.Time.AddDate(int(year), int(month), int(day)) if result.Time.Nanosecond() == 0 { result.Fsp = 0 } v.SetValue(result) return true }
func builtinDateArith(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // Op is used for distinguishing date_add and date_sub. // args[0] -> Op // args[1] -> Date // args[2] -> DateArithInterval // health check for date and interval if args[1].Kind() == types.KindNull { d.SetNull() return d, nil } nodeDate := args[1] nodeInterval := args[2].GetInterface().(ast.DateArithInterval) nodeIntervalIntervalDatum := nodeInterval.Interval.GetDatum() if nodeIntervalIntervalDatum.Kind() == types.KindNull { d.SetNull() return d, nil } // parse date fieldType := mysql.TypeDate var resultField *types.FieldType switch nodeDate.Kind() { case types.KindMysqlTime: x := nodeDate.GetMysqlTime() if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } case types.KindString: x := nodeDate.GetString() if !mysql.IsDateFormat(x) { fieldType = mysql.TypeDatetime } case types.KindInt64: x := nodeDate.GetInt64() if t, err1 := mysql.ParseTimeFromInt64(x); err1 == nil { if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } } } if mysql.IsClockUnit(nodeInterval.Unit) { fieldType = mysql.TypeDatetime } resultField = types.NewFieldType(fieldType) resultField.Decimal = mysql.MaxFsp value, err := nodeDate.ConvertTo(resultField) if err != nil { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate) } if value.Kind() == types.KindNull { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %v", value.GetValue()) } if value.Kind() != types.KindMysqlTime { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith need time type, but got %T", value.GetValue()) } result := value.GetMysqlTime() // parse interval var interval string if strings.ToLower(nodeInterval.Unit) == "day" { day, err1 := parseDayInterval(*nodeIntervalIntervalDatum) if err1 != nil { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeIntervalIntervalDatum.GetString()) } interval = fmt.Sprintf("%d", day) } else { if nodeIntervalIntervalDatum.Kind() == types.KindString { interval = fmt.Sprintf("%v", nodeIntervalIntervalDatum.GetString()) } else { ii, err1 := nodeIntervalIntervalDatum.ToInt64() if err1 != nil { d.SetNull() return d, errors.Trace(err1) } interval = fmt.Sprintf("%v", ii) } } year, month, day, duration, err := mysql.ExtractTimeValue(nodeInterval.Unit, interval) if err != nil { d.SetNull() return d, errors.Trace(err) } op := args[0].GetInterface().(ast.DateArithType) if op == ast.DateSub { year, month, day, duration = -year, -month, -day, -duration } result.Time = result.Time.Add(duration) result.Time = result.Time.AddDate(int(year), int(month), int(day)) if result.Time.Nanosecond() == 0 { result.Fsp = 0 } d.SetMysqlTime(result) return d, nil }