diff --git a/pkg/container/types/date.go b/pkg/container/types/date.go index 05aa5b79475d7841a55e07795fcaccf39b4930c3..9018ca9825390dce88ff4d3f91807b75fac9243b 100644 --- a/pkg/container/types/date.go +++ b/pkg/container/types/date.go @@ -373,3 +373,10 @@ func (d Date) Month() uint8 { _, month, _, _ := d.Calendar(true) return month } + +func LastDay(year uint16, month uint8) int { + if isLeap(int32(year)) { + return int(leapYearMonthDays[month-1]) + } + return int(flatYearMonthDays[month-1]) +} diff --git a/pkg/container/types/datetime.go b/pkg/container/types/datetime.go index 2646c4be5ee012d5c26f5f6092e6b90b8a08d9eb..2db8eea4e76317439783cf930044c01a52af914f 100644 --- a/pkg/container/types/datetime.go +++ b/pkg/container/types/datetime.go @@ -16,8 +16,9 @@ package types import ( "fmt" + "math" "strconv" - "time" + gotime "time" "unsafe" "github.com/matrixorigin/matrixone/pkg/errno" @@ -29,14 +30,21 @@ const ( secsPerHour = 60 * secsPerMinute secsPerDay = 24 * secsPerHour //secsPerWeek = 7 * secsPerDay + microSecondBitMask = 0xfffff ) // The higher 44 bits holds number of seconds since January 1, year 1 in Gregorian // calendar, and lower 20 bits holds number of microseconds func (dt Datetime) String() string { + // when datetime have microsecond, we print it, default precision is 6 y, m, d, _ := dt.ToDate().Calendar(true) hour, minute, sec := dt.Clock() + msec := int64(dt) & microSecondBitMask + if msec > 0 { + msecInstr := fmt.Sprintf("%06d", msec) + return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d"+"."+msecInstr, y, m, d, hour, minute, sec) + } return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, hour, minute, sec) } @@ -60,6 +68,8 @@ var ( // 1. all the Date value // 2. yyyy-mm-dd hh:mm:ss(.msec) // 3. yyyymmddhhmmss(.msec) +// Notice: 2022-01-01 00:00:00.1 and 20220101000000.1 should parse to 100000 microsecond instead of 1 microsecond +// I call the situation is microsecond number bug func ParseDatetime(s string) (Datetime, error) { if len(s) < 14 { if d, err := ParseDate(s); err == nil { @@ -105,6 +115,8 @@ func ParseDatetime(s string) (Datetime, error) { if err != nil { return -1, errIncorrectDatetimeValue } + // fix microsecond number bug + m = m * uint64(math.Pow10(26-len(s))) msec = uint32(m) } else { return -1, errIncorrectDatetimeValue @@ -122,6 +134,8 @@ func ParseDatetime(s string) (Datetime, error) { if err != nil { return -1, errIncorrectDatetimeValue } + // fix microsecond number bug + m = m * uint64(math.Pow10(21-len(s))) msec = uint32(m) } else { return -1, errIncorrectDatetimeValue @@ -159,7 +173,7 @@ func FromUnix(time int64) Datetime { } func Now() Datetime { - t := time.Now() + t := gotime.Now() wall := *(*uint64)(unsafe.Pointer(&t)) ext := *(*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(&t)) + unsafe.Sizeof(wall))) var sec, nsec int64 @@ -191,6 +205,63 @@ func FromClock(year int32, month, day, hour, min, sec uint8, msec uint32) Dateti return Datetime((secs << 20) + int64(msec)) } +func (dt Datetime) ConvertToGoTime() gotime.Time { + y, m, d, _ := dt.ToDate().Calendar(true) + msec := dt.microSec() + hour, min, sec := dt.Clock() + return gotime.Date(int(y), gotime.Month(m), int(d), int(hour), int(min), int(sec), int(msec*1000), startupTime.Location()) +} + +func (dt Datetime) AddDateTime(date gotime.Time, addMsec, addSec, addMin, addHour, addDay, addMonth, addYear int64) Datetime { + date = date.Add(gotime.Duration(addMsec) * gotime.Microsecond) + date = date.Add(gotime.Duration(addSec) * gotime.Second) + date = date.Add(gotime.Duration(addMin) * gotime.Minute) + date = date.Add(gotime.Duration(addHour) * gotime.Hour) + // corner case: mysql: date_add('2022-01-31',interval 1 month) -> 2022-02-28 + // only in the month year year-month + if addMonth != 0 || addYear != 0 { + originDay := date.Day() + newDate := date.AddDate(int(addYear), int(addMonth), int(addDay)) + newDay := newDate.Day() + if originDay != newDay { + maxDay := LastDay(uint16(newDate.Year()), uint8(newDate.Month()-1)) + addDay = int64(maxDay) - int64(originDay) + } + } + date = date.AddDate(int(addYear), int(addMonth), int(addDay)) + return FromClock(int32(date.Year()), uint8(date.Month()), uint8(date.Day()), uint8(date.Hour()), uint8(date.Minute()), uint8(date.Second()), uint32(date.Nanosecond()/1000)) +} + +func (dt Datetime) AddInterval(nums int64, its IntervalType) Datetime { + goTime := dt.ConvertToGoTime() + var addMsec, addSec, addMin, addHour, addDay, addMonth, addYear int64 + switch its { + case MicroSecond: + addMsec += nums + case Second: + addSec += nums + case Minute: + addMin += nums + case Hour: + addHour += nums + case Day: + addDay += nums + case Week: + addDay += 7 * nums + case Month: + addMonth += nums + case Quarter: + addMonth += 3 * nums + case Year: + addYear += nums + } + return dt.AddDateTime(goTime, addMsec, addSec, addMin, addHour, addDay, addMonth, addYear) +} + +func (dt Datetime) microSec() int64 { + return int64(dt) << 44 >> 44 +} + func (dt Datetime) sec() int64 { return int64(dt) >> 20 } diff --git a/pkg/container/types/datetime_test.go b/pkg/container/types/datetime_test.go index 37e35f01cf2eaa3ff5daa08990a338d15cd43941..f05387f52b192a7b4a7a196fa82d968200f69ebc 100644 --- a/pkg/container/types/datetime_test.go +++ b/pkg/container/types/datetime_test.go @@ -16,6 +16,7 @@ package types import ( "fmt" + "github.com/stretchr/testify/require" "testing" "time" ) @@ -67,6 +68,98 @@ func TestDatetime(t *testing.T) { fmt.Println(dt.Clock()) } +func TestAddDatetime(t *testing.T) { + addDateTimeTbl := []struct { + Input string + InputIntervalNum string + InputIntervalTypes IntervalType + expect string + }{ + {"2022-01-31 00:00:00", "1", MicroSecond, "2022-01-31 00:00:00.000001"}, + {"2022-01-31 00:00:00.000001", "1", MicroSecond, "2022-01-31 00:00:00.000002"}, + {"2022-01-31 00:00:00", "1", Second, "2022-01-31 00:00:01"}, + {"2022-01-31 00:00:00", "1", Minute, "2022-01-31 00:01:00"}, + {"2022-01-31 00:00:00", "1", Hour, "2022-01-31 01:00:00"}, + {"2022-01-31 00:00:00", "1", Day, "2022-02-01 00:00:00"}, + {"2022-01-31 00:00:00", "1", Week, "2022-02-07 00:00:00"}, + {"2022-01-01 00:00:00", "1", Month, "2022-02-01 00:00:00"}, + {"2022-01-31 00:00:00", "1", Month, "2022-02-28 00:00:00"}, + {"2022-01-01 00:00:00", "1", Quarter, "2022-04-01 00:00:00"}, + {"2022-01-31 00:00:00", "1", Quarter, "2022-04-30 00:00:00"}, + {"2022-01-01 00:00:00", "1", Year, "2023-01-01 00:00:00"}, + {"2020-02-29 00:00:00", "1", Year, "2021-02-28 00:00:00"}, + + {"2022-01-01 00:00:00", "1.1", Second_MicroSecond, "2022-01-01 00:00:01.100000"}, + {"2022-01-01 00:00:00", "1:1.1", Minute_MicroSecond, "2022-01-01 00:01:01.100000"}, + {"2022-01-01 00:00:00", "1:1", Minute_Second, "2022-01-01 00:01:01"}, + {"2022-01-01 00:00:00", "1:1:1.1", Hour_MicroSecond, "2022-01-01 01:01:01.100000"}, + {"2022-01-01 00:00:00", "1:1:1", Hour_Second, "2022-01-01 01:01:01"}, + {"2022-01-01 00:00:00", "1:1", Hour_Minute, "2022-01-01 01:01:00"}, + {"2022-01-01 00:00:00", "1 1:1:1.1", Day_MicroSecond, "2022-01-02 01:01:01.100000"}, + {"2022-01-01 00:00:00", "1 1:1:1", Day_Second, "2022-01-02 01:01:01"}, + {"2022-01-01 00:00:00", "1 1:1", Day_Minute, "2022-01-02 01:01:00"}, + {"2022-01-01 00:00:00", "1 1", Day_Hour, "2022-01-02 01:00:00"}, + {"2022-01-01 00:00:00", "1-1", Year_Month, "2023-02-01 00:00:00"}, + + {"2022-01-31 00:00:00", "1-1", Year_Month, "2023-02-28 00:00:00"}, + + {"2020-12-31 23:59:59", "1", Second, "2021-01-01 00:00:00"}, + {"2100-12-31 23:59:59", "1:1", Minute_Second, "2101-01-01 00:01:00"}, + {"1992-12-31 23:59:59.000002", "1.999999", Second_MicroSecond, "1993-01-01 00:00:01.000001"}, + {"1992-12-31 23:59:59.1", "1.1", Second_MicroSecond, "1993-01-01 00:00:00.200000"}, + } + for _, test := range addDateTimeTbl { + ret, rettype, _ := NormalizeInterval(test.InputIntervalNum, test.InputIntervalTypes) + d, err := ParseDatetime(test.Input) + require.Equal(t, err, nil) + d = d.AddInterval(ret, rettype) + require.Equal(t, d.String(), test.expect) + } +} + +func TestSubDateTime(t *testing.T) { + subDateTimeTbl := []struct { + Input string + InputIntervalNum string + InputIntervalTypes IntervalType + expect string + }{ + {"2022-01-31 00:00:00", "1", MicroSecond, "2022-01-30 23:59:59.999999"}, + {"2022-01-31 00:00:00.000001", "1", MicroSecond, "2022-01-31 00:00:00"}, + {"2022-01-31 00:00:00", "1", Second, "2022-01-30 23:59:59"}, + {"2022-01-31 00:00:00", "1", Minute, "2022-01-30 23:59:00"}, + {"2022-01-31 00:00:00", "1", Hour, "2022-01-30 23:00:00"}, + {"2022-01-31 00:00:00", "1", Day, "2022-01-30 00:00:00"}, + {"2022-01-31 00:00:00", "1", Week, "2022-01-24 00:00:00"}, + {"2022-01-01 00:00:00", "1", Month, "2021-12-01 00:00:00"}, + {"2022-03-31 00:00:00", "1", Month, "2022-02-28 00:00:00"}, + {"2022-01-01 00:00:00", "1", Quarter, "2021-10-01 00:00:00"}, + {"2022-01-31 00:00:00", "1", Quarter, "2021-10-31 00:00:00"}, + {"2022-01-01 00:00:00", "1", Year, "2021-01-01 00:00:00"}, + {"2020-02-29 00:00:00", "1", Year, "2019-02-28 00:00:00"}, + + {"2022-01-01 00:00:00", "1.1", Second_MicroSecond, "2021-12-31 23:59:58.900000"}, + {"2022-01-01 00:00:00", "1:1.1", Minute_MicroSecond, "2021-12-31 23:58:58.900000"}, + {"2022-01-01 00:00:00", "1:1", Minute_Second, "2021-12-31 23:58:59"}, + {"2022-01-01 00:00:00", "1:1:1.1", Hour_MicroSecond, "2021-12-31 22:58:58.900000"}, + {"2022-01-01 00:00:00", "1:1:1", Hour_Second, "2021-12-31 22:58:59"}, + {"2022-01-01 00:00:00", "1:1", Hour_Minute, "2021-12-31 22:59:00"}, + {"2022-01-01 00:00:00", "1 1:1:1.1", Day_MicroSecond, "2021-12-30 22:58:58.900000"}, + {"2022-01-01 00:00:00", "1 1:1:1", Day_Second, "2021-12-30 22:58:59"}, + {"2022-01-01 00:00:00", "1 1:1", Day_Minute, "2021-12-30 22:59:00"}, + {"2022-01-01 00:00:00", "1 1", Day_Hour, "2021-12-30 23:00:00"}, + {"2022-01-01 00:00:00", "1-1", Year_Month, "2020-12-01 00:00:00"}, + {"2022-01-31 00:00:00.1", "1", Second, "2022-01-30 23:59:59.100000"}, + } + for _, test := range subDateTimeTbl { + ret, rettype, _ := NormalizeInterval(test.InputIntervalNum, test.InputIntervalTypes) + d, err := ParseDatetime(test.Input) + require.Equal(t, err, nil) + d = d.AddInterval(-ret, rettype) + require.Equal(t, d.String(), test.expect) + } +} + func TestParseDatetime(t *testing.T) { tests := []struct { name string @@ -83,7 +176,7 @@ func TestParseDatetime(t *testing.T) { { name: "yyyy-mm-dd hh:mm:ss.sec", args: "1987-08-25 00:00:00.1", - want: "1987-08-25 00:00:00", + want: "1987-08-25 00:00:00.100000", }, // 2. yyyymmddhhmmss(.msec) { @@ -94,7 +187,7 @@ func TestParseDatetime(t *testing.T) { { name: "yyyymmddhhmmss.sec", args: "19870825000000.5", - want: "1987-08-25 00:00:00", + want: "1987-08-25 00:00:00.500000", }, // 3. out of range { diff --git a/pkg/container/types/interval.go b/pkg/container/types/interval.go index 0c01521b958fa89e7561bbc4e00119169e2202d6..4a934ef28c0ff5857dd2e2dc8268ad8b82d1fe5e 100644 --- a/pkg/container/types/interval.go +++ b/pkg/container/types/interval.go @@ -15,6 +15,7 @@ package types import ( + "math" "strings" "github.com/matrixorigin/matrixone/pkg/errno" @@ -111,21 +112,34 @@ func IntervalTypeOf(s string) (IntervalType, error) { // // parseInts parse integer from string s. This is used to handle interval values, +// when interval type is Second_MicroSecond Minute_MicroSecond Hour_MicroSecond Day_MicroSecond +// we should set second parameter true, other set false +// the example: when the s is "1:1" +// when we use Second_MicroSecond(...), we should parse to 1 second and 100000 microsecond. +// when we use Minute_Second(...), we just parse to 1 minute and 1 second. +// so we use method to solve this: we count the length of the num, use 1e(6 - length) * ret[len(ret) - 1] +// for example: when the s is "1:001" +// the last number length is 3, so the last number should be 1e(6 - 3) * 1 = 1000 +// typeMaxLength means when I use Second_MicroSecond, the typeMaxLength is 2 +// when s is "1", we don't think number 1 is microsecond // so there are a few strange things. // 1. Only takes 0-9, may have leading 0, still means decimal instead oct. // 2. 1-1 is parsed out as 1, 1 '-' is delim, so is '+', '.' etc. // 3. we will not support int32 overflow. // -func parseInts(s string) ([]int32, error) { +func parseInts(s string, isxxxMicrosecond bool, typeMaxLength int) ([]int32, error) { ret := make([]int32, 0) + numLength := 0 cur := -1 for _, c := range s { if c >= rune('0') && c <= rune('9') { if cur < 0 { cur = len(ret) ret = append(ret, c-rune('0')) + numLength++ } else { ret[cur] = 10*ret[cur] + c - rune('0') + numLength++ if ret[cur] < 0 { return nil, errors.New(errno.DataException, "Invalid string interval value") } @@ -133,9 +147,15 @@ func parseInts(s string) ([]int32, error) { } else { if cur >= 0 { cur = -1 + numLength = 0 } } } + if isxxxMicrosecond { + if len(ret) == typeMaxLength { + ret[len(ret)-1] *= int32(math.Pow10(6 - numLength)) + } + } return ret, nil } @@ -158,7 +178,7 @@ func conv(a []int32, mul []int64, rt IntervalType) (int64, IntervalType, error) } func NormalizeInterval(s string, it IntervalType) (ret int64, rettype IntervalType, err error) { - vals, err := parseInts(s) + vals, err := parseInts(s, isxxxMicrosecondType(it), typeMaxLength(it)) if err != nil { return } @@ -202,3 +222,49 @@ func NormalizeInterval(s string, it IntervalType) (ret int64, rettype IntervalTy } return } + +func isxxxMicrosecondType(it IntervalType) bool { + return it == Second_MicroSecond || it == Minute_MicroSecond || it == Hour_MicroSecond || it == Day_MicroSecond +} + +func typeMaxLength(it IntervalType) int { + switch it { + case MicroSecond, Second, Minute, Hour, Day, + Week, Month, Quarter, Year: + return 1 + + case Second_MicroSecond: + return 2 + + case Minute_MicroSecond: + return 3 + + case Minute_Second: + return 2 + + case Hour_MicroSecond: + return 4 + + case Hour_Second: + return 3 + + case Hour_Minute: + return 2 + + case Day_MicroSecond: + return 5 + + case Day_Second: + return 4 + + case Day_Minute: + return 3 + + case Day_Hour: + return 2 + + case Year_Month: + return 2 + } + return 0 +} diff --git a/pkg/container/types/interval_test.go b/pkg/container/types/interval_test.go index 867f01d99586e2263af5c173d8ad05d9cda12d42..ad235e7ddcc0167a52170e6824845ff2ab315521 100644 --- a/pkg/container/types/interval_test.go +++ b/pkg/container/types/interval_test.go @@ -115,12 +115,12 @@ func TestConv(t *testing.T) { val, vt, err = NormalizeInterval("1 01:02:03.4", Day_MicroSecond) val2, vt2, _ := NormalizeInterval("1 01:02:03", Day_MicroSecond) - val3, vt3, _ := NormalizeInterval("0 00:00:00.04", Day_MicroSecond) + val3, vt3, _ := NormalizeInterval("0 00:00:00.4", Day_MicroSecond) require.Equal(t, err, nil, "HM error") require.Equal(t, vt, MicroSecond, "D_US error") require.Equal(t, vt, vt2, "D_US error") require.Equal(t, vt, vt3, "D_US error") - require.Equal(t, val3, int64(4), "D_US error") + require.Equal(t, val3, int64(400000), "D_US error") require.Equal(t, val2, int64(24*60*60*1000000+1*60*60*1000000+2*60*1000000+3*1000000), "D_US error") require.Equal(t, val, val2+val3, "D_US error") diff --git a/pkg/sql/plan2/build_test.go b/pkg/sql/plan2/build_test.go index f715e0a40781c8b8d7a8d3ce021c43f3e65aba94..635e05ea7582d38a17f1a86fa86fcd527fa14dc3 100644 --- a/pkg/sql/plan2/build_test.go +++ b/pkg/sql/plan2/build_test.go @@ -30,7 +30,11 @@ import ( func TestSingleSql(t *testing.T) { // sql := "SELECT * FROM NATION where n_nationkey > 10" // sql := "SELECT COUNT(n_nationkey) FROM NATION" - sql := "SELECT COUNT(*) FROM NATION a" + // sql := "SELECT COUNT(*) FROM NATION a" + // sql := "SELECT DATE_ADD('2022-01-31', INTERVAL 1 day) FROM NATION a" + // sql := "SELECT DATE_ADD(date '2022-01-31', INTERVAL 1 day) FROM NATION" + sql := "SELECT DATE_SUB(date '2022-01-31', INTERVAL 1 day) FROM NATION" + // sql := "SELECT date'2022-01-31' + INTERVAL 114 day FROM NATION a" // sql := "SELECT n_nationkey, COUNT(*) AS TTT FROM NATION group by n_nationkey" // sql := "select * from (select * from NATION order by n_nationkey) as x where n_nationkey > 10" // sql := `select * from (select * from NATION order by n_nationkey) as x` diff --git a/pkg/sql/plan2/function/builtin/unary/date_add.go b/pkg/sql/plan2/function/builtin/unary/date_add.go new file mode 100644 index 0000000000000000000000000000000000000000..10357d2417bdb1d88373c31e1f1f5ebc08a42ab0 --- /dev/null +++ b/pkg/sql/plan2/function/builtin/unary/date_add.go @@ -0,0 +1,80 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unary + +import ( + "github.com/matrixorigin/matrixone/pkg/container/nulls" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/encoding" + "github.com/matrixorigin/matrixone/pkg/vectorize/date_add" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +func DateAdd(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + firstVector := vectors[0] + secondVector := vectors[1] + thirdVector := vectors[2] + firstValues, secondValues, thirdValues := firstVector.Col.([]types.Date), secondVector.Col.([]int64), thirdVector.Col.([]int64) + resultType := types.Type{Oid: types.T_date, Size: 4} + resultElementSize := int(resultType.Size) + if firstVector.IsScalar() { + if firstVector.ConstVectorIsNull() { + return proc.AllocScalarNullVector(resultType), nil + } + resultVector := vector.NewConst(resultType) + resultValues := make([]types.Date, 1) + vector.SetCol(resultVector, date_add.DateAdd(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } else { + resultVector, err := proc.AllocVector(resultType, int64(resultElementSize*len(firstValues))) + if err != nil { + return nil, err + } + resultValues := encoding.DecodeDateSlice(resultVector.Data) + resultValues = resultValues[:len(firstValues)] + nulls.Set(resultVector.Nsp, firstVector.Nsp) + vector.SetCol(resultVector, date_add.DateAdd(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } +} + +func DatetimeAdd(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + firstVector := vectors[0] + secondVector := vectors[1] + thirdVector := vectors[2] + firstValues, secondValues, thirdValues := firstVector.Col.([]types.Datetime), secondVector.Col.([]int64), thirdVector.Col.([]int64) + resultType := types.Type{Oid: types.T_datetime, Size: 8} + resultElementSize := int(resultType.Size) + if firstVector.IsScalar() { + if firstVector.ConstVectorIsNull() { + return proc.AllocScalarNullVector(resultType), nil + } + resultVector := vector.NewConst(resultType) + resultValues := make([]types.Datetime, 1) + vector.SetCol(resultVector, date_add.DatetimeAdd(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } else { + resultVector, err := proc.AllocVector(resultType, int64(resultElementSize*len(firstValues))) + if err != nil { + return nil, err + } + resultValues := encoding.DecodeDatetimeSlice(resultVector.Data) + resultValues = resultValues[:len(firstValues)] + nulls.Set(resultVector.Nsp, firstVector.Nsp) + vector.SetCol(resultVector, date_add.DatetimeAdd(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } +} diff --git a/pkg/sql/plan2/function/builtin/unary/date_sub.go b/pkg/sql/plan2/function/builtin/unary/date_sub.go new file mode 100644 index 0000000000000000000000000000000000000000..27ac6a96b87290ba51497f532db85bd5aaf0cd70 --- /dev/null +++ b/pkg/sql/plan2/function/builtin/unary/date_sub.go @@ -0,0 +1,80 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unary + +import ( + "github.com/matrixorigin/matrixone/pkg/container/nulls" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/encoding" + "github.com/matrixorigin/matrixone/pkg/vectorize/date_sub" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +func DateSub(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + firstVector := vectors[0] + secondVector := vectors[1] + thirdVector := vectors[2] + firstValues, secondValues, thirdValues := firstVector.Col.([]types.Date), secondVector.Col.([]int64), thirdVector.Col.([]int64) + resultType := types.Type{Oid: types.T_date, Size: 4} + resultElementSize := int(resultType.Size) + if firstVector.IsScalar() { + if firstVector.ConstVectorIsNull() { + return proc.AllocScalarNullVector(resultType), nil + } + resultVector := vector.NewConst(resultType) + resultValues := make([]types.Date, 1) + vector.SetCol(resultVector, date_sub.DateSub(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } else { + resultVector, err := proc.AllocVector(resultType, int64(resultElementSize*len(firstValues))) + if err != nil { + return nil, err + } + resultValues := encoding.DecodeDateSlice(resultVector.Data) + resultValues = resultValues[:len(firstValues)] + nulls.Set(resultVector.Nsp, firstVector.Nsp) + vector.SetCol(resultVector, date_sub.DateSub(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } +} + +func DatetimeSub(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + firstVector := vectors[0] + secondVector := vectors[1] + thirdVector := vectors[2] + firstValues, secondValues, thirdValues := firstVector.Col.([]types.Datetime), secondVector.Col.([]int64), thirdVector.Col.([]int64) + resultType := types.Type{Oid: types.T_datetime, Size: 8} + resultElementSize := int(resultType.Size) + if firstVector.IsScalar() { + if firstVector.ConstVectorIsNull() { + return proc.AllocScalarNullVector(resultType), nil + } + resultVector := vector.NewConst(resultType) + resultValues := make([]types.Datetime, 1) + vector.SetCol(resultVector, date_sub.DatetimeSub(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } else { + resultVector, err := proc.AllocVector(resultType, int64(resultElementSize*len(firstValues))) + if err != nil { + return nil, err + } + resultValues := encoding.DecodeDatetimeSlice(resultVector.Data) + resultValues = resultValues[:len(firstValues)] + nulls.Set(resultVector.Nsp, firstVector.Nsp) + vector.SetCol(resultVector, date_sub.DatetimeSub(firstValues, secondValues, thirdValues, resultValues)) + return resultVector, nil + } +} diff --git a/pkg/sql/plan2/function/buitins.go b/pkg/sql/plan2/function/buitins.go index fc8ba7f04ec60b70bf13030fef74fe18b5f6f34e..7a38ec2335afd7b819382e2a73b58ce001adfb46 100644 --- a/pkg/sql/plan2/function/buitins.go +++ b/pkg/sql/plan2/function/buitins.go @@ -1581,7 +1581,17 @@ var builtins = map[int][]Function{ Args: []types.T{types.T_date, types.T_int64, types.T_int64}, ReturnTyp: types.T_date, TypeCheckFn: strictTypeCheck, - Fn: nil, + Fn: unary.DateAdd, + }, + { + Index: 1, + Volatile: true, + Flag: plan.Function_STRICT, + Layout: STANDARD_FUNCTION, + Args: []types.T{types.T_datetime, types.T_int64, types.T_int64}, + ReturnTyp: types.T_datetime, + TypeCheckFn: strictTypeCheck, + Fn: unary.DatetimeAdd, }, }, DATE_SUB: { @@ -1593,7 +1603,17 @@ var builtins = map[int][]Function{ Args: []types.T{types.T_date, types.T_int64, types.T_int64}, ReturnTyp: types.T_date, TypeCheckFn: strictTypeCheck, - Fn: nil, + Fn: unary.DateSub, + }, + { + Index: 1, + Volatile: true, + Flag: plan.Function_STRICT, + Layout: STANDARD_FUNCTION, + Args: []types.T{types.T_datetime, types.T_int64, types.T_int64}, + ReturnTyp: types.T_datetime, + TypeCheckFn: strictTypeCheck, + Fn: unary.DatetimeSub, }, }, TAN: { diff --git a/pkg/sql/unittest/datatype_test.go b/pkg/sql/unittest/datatype_test.go index 1a8dc3a822123e585459fcfadc09579e0d73ee3c..76357ed965b4e96615b7c711fa387d0f3842e6c8 100644 --- a/pkg/sql/unittest/datatype_test.go +++ b/pkg/sql/unittest/datatype_test.go @@ -246,7 +246,7 @@ func TestDatetimeType(t *testing.T) { {sql: "select * from tbl2;", res: executeResult{ attr: []string{"a"}, data: [][]string{ - {"2018-04-28 10:21:15"}, {"2017-04-28 03:05:01"}, {"2025-07-16 16:39:58"}, {"2021-12-03 14:56:33"}, {"null"}, + {"2018-04-28 10:21:15.123000"}, {"2017-04-28 03:05:01.456000"}, {"2025-07-16 16:39:58.567000"}, {"2021-12-03 14:56:33.890000"}, {"null"}, }, }, com: "that is disputed. what does the msec do?"}, {sql: "select * from tbl3;", res: executeResult{ diff --git a/pkg/vectorize/date_add/date_add.go b/pkg/vectorize/date_add/date_add.go new file mode 100644 index 0000000000000000000000000000000000000000..a9a5dad49ccb18980d246828ba92866965c94f6e --- /dev/null +++ b/pkg/vectorize/date_add/date_add.go @@ -0,0 +1,40 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package date_add + +import "github.com/matrixorigin/matrixone/pkg/container/types" + +var ( + DateAdd func([]types.Date, []int64, []int64, []types.Date) []types.Date + DatetimeAdd func([]types.Datetime, []int64, []int64, []types.Datetime) []types.Datetime +) + +func init() { + DateAdd = dateAdd + DatetimeAdd = datetimeAdd +} + +func dateAdd(xs []types.Date, ys []int64, zs []int64, rs []types.Date) []types.Date { + for i, d := range xs { + rs[i] = d.ToTime().AddInterval(ys[i], types.IntervalType(zs[i])).ToDate() + } + return rs +} + +func datetimeAdd(xs []types.Datetime, ys []int64, zs []int64, rs []types.Datetime) []types.Datetime { + for i, d := range xs { + rs[i] = d.AddInterval(ys[i], types.IntervalType(zs[i])) + } + return rs +} diff --git a/pkg/vectorize/date_add/date_add_test.go b/pkg/vectorize/date_add/date_add_test.go new file mode 100644 index 0000000000000000000000000000000000000000..72b4bf9dc72fb6daddf69898096982944b6c7934 --- /dev/null +++ b/pkg/vectorize/date_add/date_add_test.go @@ -0,0 +1,83 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package date_add + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func TestDateAdd(t *testing.T) { + testCases := []struct { + name string + args1 []types.Date + args2 []int64 + args3 []int64 + want []types.Date + }{ + { + args1: []types.Date{types.FromCalendar(2021, 8, 13)}, + args2: []int64{1}, + args3: []int64{int64(types.Day)}, + want: []types.Date{types.FromCalendar(2021, 8, 14)}, + }, + { + args1: []types.Date{types.FromCalendar(2021, 1, 31)}, + args2: []int64{1}, + args3: []int64{int64(types.Month)}, + want: []types.Date{types.FromCalendar(2021, 2, 28)}, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + got := make([]types.Date, len(c.args1)) + require.Equal(t, c.want, dateAdd(c.args1, c.args2, c.args3, got)) + }) + } + +} + +func TestDatetimeAdd(t *testing.T) { + testCases := []struct { + name string + args1 []types.Datetime + args2 []int64 + args3 []int64 + want []types.Datetime + }{ + { + args1: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 1)}, + args2: []int64{1}, + args3: []int64{int64(types.MicroSecond)}, + want: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 2)}, + }, + { + args1: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 1)}, + args2: []int64{1}, + args3: []int64{int64(types.Second)}, + want: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 2, 1)}, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + got := make([]types.Datetime, len(c.args1)) + require.Equal(t, c.want, datetimeAdd(c.args1, c.args2, c.args3, got)) + }) + } + +} diff --git a/pkg/vectorize/date_sub/date_sub.go b/pkg/vectorize/date_sub/date_sub.go new file mode 100644 index 0000000000000000000000000000000000000000..408850434d06155ea4f85b695ae27eb84e87efa9 --- /dev/null +++ b/pkg/vectorize/date_sub/date_sub.go @@ -0,0 +1,40 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package date_sub + +import "github.com/matrixorigin/matrixone/pkg/container/types" + +var ( + DateSub func([]types.Date, []int64, []int64, []types.Date) []types.Date + DatetimeSub func([]types.Datetime, []int64, []int64, []types.Datetime) []types.Datetime +) + +func init() { + DateSub = dateSub + DatetimeSub = datetimeSub +} + +func dateSub(xs []types.Date, ys []int64, zs []int64, rs []types.Date) []types.Date { + for i, d := range xs { + rs[i] = d.ToTime().AddInterval(-ys[i], types.IntervalType(zs[i])).ToDate() + } + return rs +} + +func datetimeSub(xs []types.Datetime, ys []int64, zs []int64, rs []types.Datetime) []types.Datetime { + for i, d := range xs { + rs[i] = d.AddInterval(-ys[i], types.IntervalType(zs[i])) + } + return rs +} diff --git a/pkg/vectorize/date_sub/date_sub_test.go b/pkg/vectorize/date_sub/date_sub_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3a08fa87742dc6aa5f758ac6b11ff1ef11c75902 --- /dev/null +++ b/pkg/vectorize/date_sub/date_sub_test.go @@ -0,0 +1,83 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package date_sub + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func TestDateSub(t *testing.T) { + testCases := []struct { + name string + args1 []types.Date + args2 []int64 + args3 []int64 + want []types.Date + }{ + { + args1: []types.Date{types.FromCalendar(2021, 8, 13)}, + args2: []int64{1}, + args3: []int64{int64(types.Day)}, + want: []types.Date{types.FromCalendar(2021, 8, 12)}, + }, + { + args1: []types.Date{types.FromCalendar(2021, 1, 1)}, + args2: []int64{1}, + args3: []int64{int64(types.Day)}, + want: []types.Date{types.FromCalendar(2020, 12, 31)}, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + got := make([]types.Date, len(c.args1)) + require.Equal(t, c.want, dateSub(c.args1, c.args2, c.args3, got)) + }) + } + +} + +func TestDatetimeSub(t *testing.T) { + testCases := []struct { + name string + args1 []types.Datetime + args2 []int64 + args3 []int64 + want []types.Datetime + }{ + { + args1: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 1)}, + args2: []int64{1}, + args3: []int64{int64(types.MicroSecond)}, + want: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 0)}, + }, + { + args1: []types.Datetime{types.FromClock(2020, 1, 1, 1, 1, 1, 1)}, + args2: []int64{2}, + args3: []int64{int64(types.Second)}, + want: []types.Datetime{types.FromClock(2020, 1, 1, 1, 0, 59, 1)}, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + got := make([]types.Datetime, len(c.args1)) + require.Equal(t, c.want, datetimeSub(c.args1, c.args2, c.args3, got)) + }) + } + +}