github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/function/builtin/binary/str_to_date.go (about)

     1  // Copyright 2021 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 binary
    16  
    17  import (
    18  	"context"
    19  	"strings"
    20  	"unicode"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/container/nulls"
    24  	"github.com/matrixorigin/matrixone/pkg/container/types"
    25  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    26  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    27  )
    28  
    29  const (
    30  	// MaxFsp is the maximum digit of fractional seconds part.
    31  	MaxFsp = 6
    32  )
    33  
    34  // Convert the string to date type value according to the format string
    35  func StrToDate(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) {
    36  	dateVector := vectors[0]
    37  	formatVector := vectors[1]
    38  
    39  	resultType := types.T_date.ToType()
    40  	if !formatVector.IsScalar() {
    41  		return nil, moerr.NewInvalidArg(proc.Ctx, "to_date format", "not constant")
    42  	}
    43  	if dateVector.IsScalarNull() || formatVector.IsScalarNull() {
    44  		return proc.AllocScalarNullVector(resultType), nil
    45  	}
    46  	// get the format string.
    47  	formatMask := formatVector.GetString(0)
    48  
    49  	if dateVector.IsScalar() {
    50  		datestr := dateVector.GetString(0)
    51  		ctx := make(map[string]int)
    52  		time := NewGeneralTime()
    53  		success := strToDate(proc.Ctx, time, datestr, formatMask, ctx)
    54  		if !success {
    55  			// should be null
    56  			return proc.AllocScalarNullVector(resultType), nil
    57  		} else {
    58  			if types.ValidDate(int32(time.year), time.month, time.day) {
    59  				resCol := types.DateFromCalendar(int32(time.year), time.month, time.day)
    60  				return vector.NewConstFixed[types.Date](resultType, 1, resCol, proc.Mp()), nil
    61  			} else {
    62  				// should be null
    63  				return proc.AllocScalarNullVector(resultType), nil
    64  			}
    65  		}
    66  	} else {
    67  		datestrs := vector.MustStrCols(dateVector)
    68  		rNsp := nulls.NewWithSize(len(datestrs))
    69  		resCol, err := CalcStrToDate(proc.Ctx, datestrs, formatMask, dateVector.Nsp, rNsp)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		resultVector := vector.NewWithFixed[types.Date](resultType, resCol, rNsp, proc.Mp())
    74  		nulls.Set(resultVector.Nsp, dateVector.Nsp)
    75  		return resultVector, nil
    76  	}
    77  }
    78  
    79  // Convert the string to datetime type value according to the format string
    80  func StrToDateTime(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) {
    81  	dateVector := vectors[0]
    82  	formatVector := vectors[1]
    83  
    84  	resultType := types.T_datetime.ToType()
    85  	if !formatVector.IsScalar() {
    86  		return nil, moerr.NewInvalidArg(proc.Ctx, "to_date format", "not constant")
    87  	}
    88  	if dateVector.IsScalarNull() || formatVector.IsScalarNull() {
    89  		return proc.AllocScalarNullVector(resultType), nil
    90  	}
    91  	// get the format string.
    92  	formatMask := formatVector.GetString(0)
    93  
    94  	if dateVector.IsScalar() {
    95  		datetimestr := dateVector.GetString(0)
    96  		ctx := make(map[string]int)
    97  		time := NewGeneralTime()
    98  		success := strToDate(proc.Ctx, time, datetimestr, formatMask, ctx)
    99  		if !success {
   100  			// should be null
   101  			return proc.AllocScalarNullVector(resultType), nil
   102  		} else {
   103  			if types.ValidDatetime(int32(time.year), time.month, time.day) && types.ValidTimeInDay(time.hour, time.minute, time.second) {
   104  				resCol := types.DatetimeFromClock(int32(time.year), time.month, time.day, time.hour, time.minute, time.second, time.microsecond)
   105  				return vector.NewConstFixed[types.Datetime](resultType, 1, resCol, proc.Mp()), nil
   106  			} else {
   107  				// should be null
   108  				return proc.AllocScalarNullVector(resultType), nil
   109  			}
   110  		}
   111  	} else {
   112  		datetimestrs := vector.MustStrCols(dateVector)
   113  		rNsp := nulls.NewWithSize(len(datetimestrs))
   114  		resCol, err := CalcStrToDatetime(proc.Ctx, datetimestrs, formatMask, dateVector.Nsp, rNsp)
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		resultVector := vector.NewWithFixed[types.Datetime](resultType, resCol, nulls.NewWithSize(len(resCol)), proc.Mp())
   119  		nulls.Set(resultVector.Nsp, dateVector.Nsp)
   120  		return resultVector, nil
   121  	}
   122  }
   123  
   124  // // Convert the string to time type value according to the format string,such as '09:30:17'
   125  func StrToTime(vectors []*vector.Vector, proc *process.Process) (*vector.Vector, error) {
   126  	dateVector := vectors[0]
   127  	formatVector := vectors[1]
   128  
   129  	resultType := types.T_time.ToType()
   130  	if !formatVector.IsScalar() {
   131  		return nil, moerr.NewInvalidArg(proc.Ctx, "to_date format", "not constant")
   132  	}
   133  	if dateVector.IsScalarNull() || formatVector.IsScalarNull() {
   134  		return proc.AllocScalarNullVector(resultType), nil
   135  	}
   136  	// get the format string.
   137  	formatMask := formatVector.GetString(0)
   138  
   139  	if dateVector.IsScalar() {
   140  		timestr := dateVector.GetString(0)
   141  
   142  		ctx := make(map[string]int)
   143  		time := NewGeneralTime()
   144  		success := strToDate(proc.Ctx, time, timestr, formatMask, ctx)
   145  		if !success {
   146  			// should be null
   147  			return proc.AllocScalarNullVector(resultType), nil
   148  		} else {
   149  			if types.ValidTime(uint64(time.hour), uint64(time.minute), uint64(time.second)) {
   150  				resCol := types.TimeFromClock(false, uint64(time.hour), time.minute, time.second, time.microsecond)
   151  				return vector.NewConstFixed[types.Time](resultType, 1, resCol, proc.Mp()), nil
   152  			} else {
   153  				// should be null
   154  				return proc.AllocScalarNullVector(resultType), nil
   155  			}
   156  		}
   157  	} else {
   158  		timestrs := vector.MustStrCols(dateVector)
   159  		rNsp := nulls.NewWithSize(len(timestrs))
   160  		resCol, err := CalcStrToTime(proc.Ctx, timestrs, formatMask, dateVector.Nsp, rNsp)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		resultVector := vector.NewWithFixed[types.Time](resultType, resCol, nulls.NewWithSize(len(resCol)), proc.Mp())
   165  		nulls.Set(resultVector.Nsp, dateVector.Nsp)
   166  		return resultVector, nil
   167  	}
   168  }
   169  
   170  func CalcStrToDatetime(ctx context.Context, timestrs []string, format string, ns *nulls.Nulls, rNsp *nulls.Nulls) ([]types.Datetime, error) {
   171  	res := make([]types.Datetime, len(timestrs))
   172  	time := NewGeneralTime()
   173  	for idx, timestr := range timestrs {
   174  		if nulls.Contains(ns, uint64(idx)) {
   175  			continue
   176  		}
   177  		success := CoreStrToDate(ctx, time, timestr, format)
   178  		if !success {
   179  			// should be null
   180  			nulls.Add(rNsp, uint64(idx))
   181  		} else {
   182  			if types.ValidDatetime(int32(time.year), time.month, time.day) && types.ValidTimeInDay(time.hour, time.minute, time.second) {
   183  				res[idx] = types.DatetimeFromClock(int32(time.year), time.month, time.day, time.hour, time.minute, time.second, time.microsecond)
   184  			} else {
   185  				// should be null
   186  				nulls.Add(rNsp, uint64(idx))
   187  			}
   188  		}
   189  		time.ResetTime()
   190  	}
   191  	return res, nil
   192  }
   193  
   194  func CalcStrToDate(ctx context.Context, timestrs []string, format string, ns *nulls.Nulls, rNsp *nulls.Nulls) ([]types.Date, error) {
   195  	res := make([]types.Date, len(timestrs))
   196  	time := NewGeneralTime()
   197  	for idx, timestr := range timestrs {
   198  		if nulls.Contains(ns, uint64(idx)) {
   199  			continue
   200  		}
   201  		success := CoreStrToDate(ctx, time, timestr, format)
   202  		if !success {
   203  			// should be null
   204  			nulls.Add(rNsp, uint64(idx))
   205  		} else {
   206  			if types.ValidDate(int32(time.year), time.month, time.day) {
   207  				res[idx] = types.DateFromCalendar(int32(time.year), time.month, time.day)
   208  			} else {
   209  				// should be null
   210  				nulls.Add(rNsp, uint64(idx))
   211  			}
   212  		}
   213  		time.ResetTime()
   214  	}
   215  	return res, nil
   216  }
   217  
   218  func CalcStrToTime(ctx context.Context, timestrs []string, format string, ns *nulls.Nulls, rNsp *nulls.Nulls) ([]types.Time, error) {
   219  	res := make([]types.Time, len(timestrs))
   220  	time := NewGeneralTime()
   221  	for idx, timestr := range timestrs {
   222  		if nulls.Contains(ns, uint64(idx)) {
   223  			continue
   224  		}
   225  		success := CoreStrToDate(ctx, time, timestr, format)
   226  		if !success {
   227  			// should be null
   228  			nulls.Add(rNsp, uint64(idx))
   229  		} else {
   230  			if types.ValidTime(uint64(time.hour), uint64(time.minute), uint64(time.second)) {
   231  				res[idx] = types.TimeFromClock(false, uint64(time.hour), time.minute, time.second, time.microsecond)
   232  			} else {
   233  				// should be null
   234  				nulls.Add(rNsp, uint64(idx))
   235  			}
   236  		}
   237  		time.ResetTime()
   238  	}
   239  	return res, nil
   240  }
   241  
   242  func CoreStrToDate(cctx context.Context, t *GeneralTime, date string, format string) bool {
   243  	ctx := make(map[string]int)
   244  	success := strToDate(cctx, t, date, format, ctx)
   245  	if !success {
   246  		return false
   247  	}
   248  	if err := checkMysqlTime(cctx, t, ctx); err != nil {
   249  		return false
   250  	}
   251  	return true
   252  }
   253  
   254  // strToDate converts date string according to format,
   255  // the value will be stored in argument ctx. the second return value is true when success
   256  func strToDate(cctx context.Context, t *GeneralTime, date string, format string, ctx map[string]int) (success bool) {
   257  	date = trimWhiteSpace(date)
   258  	format = trimWhiteSpace(format)
   259  
   260  	token, formatRemain, succ := nextFormatToken(format)
   261  	if !succ {
   262  		return false
   263  	}
   264  
   265  	if token == "" {
   266  		if len(date) != 0 {
   267  			// Extra characters at the end of date are ignored
   268  			return true
   269  		}
   270  		// Normal case. Both token and date are empty now.
   271  		return true
   272  	}
   273  
   274  	if len(date) == 0 {
   275  		ctx[token] = 0
   276  		return true
   277  	}
   278  
   279  	dateRemain, succ := matchDateWithToken(t, date, token, ctx)
   280  	if !succ {
   281  		return false
   282  	}
   283  
   284  	return strToDate(cctx, t, dateRemain, formatRemain, ctx)
   285  }
   286  
   287  // checkMysqlTime fixes the Time use the values in the context.
   288  func checkMysqlTime(cctx context.Context, t *GeneralTime, ctx map[string]int) error {
   289  	if valueAMorPm, ok := ctx["%p"]; ok {
   290  		if _, ok := ctx["%H"]; ok {
   291  			return moerr.NewInternalError(cctx, "Truncated incorrect %-.64s value: '%-.128s'", "time", t)
   292  		}
   293  		if t.getHour() == 0 {
   294  			return moerr.NewInternalError(cctx, "Truncated incorrect %-.64s value: '%-.128s'", "time", t)
   295  		}
   296  		if t.getHour() == 12 {
   297  			// 12 is a special hour.
   298  			switch valueAMorPm {
   299  			case timeOfAM:
   300  				t.setHour(0)
   301  			case timeOfPM:
   302  				t.setHour(12)
   303  			}
   304  			return nil
   305  		}
   306  		if valueAMorPm == timeOfPM {
   307  			t.setHour(t.getHour() + 12)
   308  		}
   309  	} else {
   310  		if _, ok := ctx["%h"]; ok && t.getHour() == 12 {
   311  			t.setHour(0)
   312  		}
   313  	}
   314  	return nil
   315  }
   316  
   317  // Judge the return value type of the str_to_date function according to the value of the fromat parameter
   318  func JudgmentToDateReturnType(format string) (tp types.T, fsp int) {
   319  	isTime, isDate := GetTimeFormatType(format)
   320  	if isTime && !isDate {
   321  		tp = types.T_time
   322  	} else if !isTime && isDate {
   323  		tp = types.T_date
   324  	} else {
   325  		tp = types.T_datetime
   326  	}
   327  	if strings.Contains(format, "%f") {
   328  		fsp = MaxFsp
   329  	}
   330  	return tp, MaxFsp
   331  }
   332  
   333  // GetTimeFormatType checks the type(Time, Date or Datetime) of a format string.
   334  func GetTimeFormatType(format string) (isTime, isDate bool) {
   335  	format = trimWhiteSpace(format)
   336  	var token string
   337  	var succ bool
   338  	for {
   339  		token, format, succ = nextFormatToken(format)
   340  		if len(token) == 0 {
   341  			break
   342  		}
   343  		if !succ {
   344  			isTime, isDate = false, false
   345  			break
   346  		}
   347  		if len(token) >= 2 && token[0] == '%' {
   348  			switch token[1] {
   349  			case 'h', 'H', 'i', 'I', 's', 'S', 'k', 'l', 'f', 'r', 'T':
   350  				isTime = true
   351  			case 'y', 'Y', 'm', 'M', 'c', 'b', 'D', 'd', 'e':
   352  				isDate = true
   353  			}
   354  		}
   355  		if isTime && isDate {
   356  			break
   357  		}
   358  	}
   359  	return
   360  }
   361  
   362  // trim spaces in strings
   363  func trimWhiteSpace(input string) string {
   364  	for i, c := range input {
   365  		if !unicode.IsSpace(c) {
   366  			return input[i:]
   367  		}
   368  	}
   369  	return ""
   370  }
   371  
   372  // nextFormatToken takes next one format control token from the string.
   373  // such as: format "%d %H %m" will get token "%d" and the remain is " %H %m".
   374  func nextFormatToken(format string) (token string, remain string, success bool) {
   375  	if len(format) == 0 {
   376  		return "", "", true
   377  	}
   378  
   379  	// Just one character.
   380  	if len(format) == 1 {
   381  		if format[0] == '%' {
   382  			return "", "", false
   383  		}
   384  		return format, "", true
   385  	}
   386  
   387  	// More than one character.
   388  	if format[0] == '%' {
   389  		return format[:2], format[2:], true
   390  	}
   391  
   392  	return format[:1], format[1:], true
   393  }