github.com/matrixorigin/matrixone@v1.2.0/pkg/container/types/interval.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package types 16 17 import ( 18 "math" 19 "strings" 20 21 "github.com/matrixorigin/matrixone/pkg/common/moerr" 22 ) 23 24 /* 25 * Interval in MySQL is not a real type, that is, it cannot be stored in 26 * a table as column. We still treat it as if it is a type so that we 27 * can use it in some functions. 28 */ 29 30 type IntervalType int8 31 32 const IntervalNumMAX = int64(^uint64(0) >> 21) 33 34 const ( 35 IntervalTypeInvalid IntervalType = iota 36 MicroSecond 37 Second 38 Minute 39 Hour 40 Day 41 Week 42 Month 43 Quarter 44 Year 45 Second_MicroSecond 46 Minute_MicroSecond 47 Minute_Second 48 Hour_MicroSecond 49 Hour_Second 50 Hour_Minute 51 Day_MicroSecond 52 Day_Second 53 Day_Minute 54 Day_Hour 55 Year_Month 56 IntervalTypeMax 57 ) 58 59 func (it IntervalType) String() string { 60 switch it { 61 case MicroSecond: 62 return "MICROSECOND" 63 case Second: 64 return "SECOND" 65 case Minute: 66 return "MINUTE" 67 case Hour: 68 return "HOUR" 69 case Day: 70 return "DAY" 71 case Week: 72 return "WEEK" 73 case Month: 74 return "MONTH" 75 case Quarter: 76 return "QUARTER" 77 case Year: 78 return "YEAR" 79 case Second_MicroSecond: 80 return "SECOND_MICROSECOND" 81 case Minute_MicroSecond: 82 return "MINUTE_MICROSECOND" 83 case Minute_Second: 84 return "MINUTE_SECOND" 85 case Hour_MicroSecond: 86 return "HOUR_MICROSECOND" 87 case Hour_Second: 88 return "HOUR_SECOND" 89 case Hour_Minute: 90 return "HOUR_MINUTE" 91 case Day_MicroSecond: 92 return "DAY_MICROSECOND" 93 case Day_Second: 94 return "DAY_SECOND" 95 case Day_Minute: 96 return "DAY_MINUTE" 97 case Day_Hour: 98 return "DAY_HOUR" 99 case Year_Month: 100 return "YEAR_MONTH" 101 } 102 return "INVALID_INTERVAL_TYPE" 103 } 104 105 func IntervalTypeOf(s string) (IntervalType, error) { 106 for i := 1; i < int(IntervalTypeMax); i++ { 107 if IntervalType(i).String() == strings.ToUpper(s) { 108 return IntervalType(i), nil 109 } 110 } 111 return IntervalTypeMax, moerr.NewInvalidInputNoCtx("invalid interval type '%s'", s) 112 } 113 114 // parseInts parse integer from string s. This is used to handle interval values, 115 // when interval type is Second_MicroSecond Minute_MicroSecond Hour_MicroSecond Day_MicroSecond 116 // we should set second parameter true, other set false 117 // the example: when the s is "1:1" 118 // when we use Second_MicroSecond(...), we should parse to 1 second and 100000 microsecond. 119 // when we use Minute_Second(...), we just parse to 1 minute and 1 second. 120 // so we use method to solve this: we count the length of the num, use 1e(6 - length) * ret[len(ret) - 1] 121 // for example: when the s is "1:001" 122 // the last number length is 3, so the last number should be 1e(6 - 3) * 1 = 1000 123 // so there are a few strange things. 124 // 1. Only takes 0-9, may have leading 0, still means decimal instead oct. 125 // 2. 1-1 is parsed out as 1, 1 '-' is delim, so is '+', '.' etc. 126 // 3. we will not support int32 overflow. 127 func parseInts(s string, isxxxMicrosecond bool, typeMaxLength int) ([]int64, error) { 128 ret := make([]int64, 0) 129 numLength := 0 130 cur := -1 131 for _, c := range s { 132 if c >= rune('0') && c <= rune('9') { 133 if cur < 0 { 134 cur = len(ret) 135 ret = append(ret, int64(c-rune('0'))) 136 numLength++ 137 } else { 138 ret[cur] = 10*ret[cur] + int64(c-rune('0')) 139 numLength++ 140 if ret[cur] < 0 { 141 return nil, moerr.NewInvalidInputNoCtx("invalid time interval value '%s'", s) 142 } 143 } 144 } else { 145 if cur >= 0 { 146 cur = -1 147 numLength = 0 148 } 149 } 150 } 151 if isxxxMicrosecond { 152 if len(ret) == typeMaxLength { 153 ret[len(ret)-1] *= int64(math.Pow10(6 - numLength)) 154 } 155 } 156 // parse "-1:1" 157 for _, c := range s { 158 if c == ' ' { 159 continue 160 } else if c == '-' { 161 for i := range ret { 162 ret[i] = -ret[i] 163 } 164 break 165 } else { 166 break 167 } 168 } 169 return ret, nil 170 } 171 172 func conv(a []int64, mul []int64, rt IntervalType) (int64, IntervalType, error) { 173 if len(a) != len(mul) { 174 return 0, IntervalTypeInvalid, moerr.NewInternalErrorNoCtx("conv intervaltype has jagged array input") 175 } 176 177 var largerThanZero bool 178 for _, num := range a { 179 if num > 0 || num < 0 { 180 largerThanZero = num > 0 181 } 182 } 183 var ret int64 184 var curMul int64 = 1 185 186 for i := len(a) - 1; i >= 0; i-- { 187 curMul = curMul * mul[i] 188 ret += int64(a[i]) * curMul 189 } 190 if largerThanZero && ret < 0 { 191 return 0, IntervalTypeInvalid, moerr.NewInvalidInputNoCtx("interval type, bad value '%d'", ret) 192 } else if !largerThanZero && ret > 0 { 193 return 0, IntervalTypeInvalid, moerr.NewInvalidInputNoCtx("interval type, bad value '%d'", ret) 194 } 195 196 return ret, rt, nil 197 } 198 199 func NormalizeInterval(s string, it IntervalType) (ret int64, rettype IntervalType, err error) { 200 vals, err := parseInts(s, isxxxMicrosecondType(it), typeMaxLength(it)) 201 if err != nil { 202 return 203 } 204 205 switch it { 206 case MicroSecond, Second, Minute, Hour, Day, 207 Week, Month, Quarter, Year: 208 ret, rettype, err = conv(vals, []int64{1}, it) 209 210 case Second_MicroSecond: 211 ret, rettype, err = conv(vals, []int64{1000000, 1}, MicroSecond) 212 213 case Minute_MicroSecond: 214 ret, rettype, err = conv(vals, []int64{60, 1000000, 1}, MicroSecond) 215 216 case Minute_Second: 217 ret, rettype, err = conv(vals, []int64{60, 1}, Second) 218 219 case Hour_MicroSecond: 220 ret, rettype, err = conv(vals, []int64{60, 60, 1000000, 1}, MicroSecond) 221 222 case Hour_Second: 223 ret, rettype, err = conv(vals, []int64{60, 60, 1}, Second) 224 225 case Hour_Minute: 226 ret, rettype, err = conv(vals, []int64{60, 1}, Minute) 227 228 case Day_MicroSecond: 229 ret, rettype, err = conv(vals, []int64{24, 60, 60, 1000000, 1}, MicroSecond) 230 231 case Day_Second: 232 ret, rettype, err = conv(vals, []int64{24, 60, 60, 1}, Second) 233 234 case Day_Minute: 235 ret, rettype, err = conv(vals, []int64{24, 60, 1}, Minute) 236 237 case Day_Hour: 238 ret, rettype, err = conv(vals, []int64{24, 1}, Hour) 239 case Year_Month: 240 ret, rettype, err = conv(vals, []int64{12, 1}, Month) 241 } 242 return 243 } 244 245 func isxxxMicrosecondType(it IntervalType) bool { 246 return it == Second_MicroSecond || it == Minute_MicroSecond || it == Hour_MicroSecond || it == Day_MicroSecond 247 } 248 249 func typeMaxLength(it IntervalType) int { 250 switch it { 251 case MicroSecond, Second, Minute, Hour, Day, 252 Week, Month, Quarter, Year: 253 return 1 254 255 case Second_MicroSecond: 256 return 2 257 258 case Minute_MicroSecond: 259 return 3 260 261 case Minute_Second: 262 return 2 263 264 case Hour_MicroSecond: 265 return 4 266 267 case Hour_Second: 268 return 3 269 270 case Hour_Minute: 271 return 2 272 273 case Day_MicroSecond: 274 return 5 275 276 case Day_Second: 277 return 4 278 279 case Day_Minute: 280 return 3 281 282 case Day_Hour: 283 return 2 284 285 case Year_Month: 286 return 2 287 } 288 return 0 289 } 290 291 // UnitIsDayOrLarger if interval type unit is day or larger, we return true 292 // else return false 293 // use to judge a string whether it needs to become date/datetime type when we use date_add/sub(str string, interval type) 294 func UnitIsDayOrLarger(it IntervalType) bool { 295 return it == Day || it == Week || it == Month || it == Quarter || it == Year || it == Year_Month 296 } 297 298 func JudgeIntervalNumOverflow(num int64, it IntervalType) error { 299 if it == MicroSecond { 300 return nil 301 } else if num > int64(IntervalNumMAX) { 302 return moerr.NewInvalidArgNoCtx("interval", num) 303 } else if -num > int64(IntervalNumMAX) { 304 return moerr.NewInvalidArgNoCtx("interval", num) 305 } 306 return nil 307 }