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  }