github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/function/func_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 function
    16  
    17  import (
    18  	"context"
    19  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    20  	"github.com/matrixorigin/matrixone/pkg/container/types"
    21  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    22  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function/functionUtil"
    23  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    24  	"unicode"
    25  )
    26  
    27  func builtInStrToDate(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error {
    28  	p1 := vector.GenerateFunctionStrParameter(parameters[0])
    29  	p2 := vector.GenerateFunctionStrParameter(parameters[1])
    30  
    31  	rs := vector.MustFunctionResult[types.Date](result)
    32  
    33  	time := NewGeneralTime()
    34  	for i := uint64(0); i < uint64(length); i++ {
    35  		v1, null1 := p1.GetStrValue(i)
    36  		v2, null2 := p2.GetStrValue(i)
    37  		if null1 || null2 {
    38  			if err := rs.Append(0, true); err != nil {
    39  				return err
    40  			}
    41  		} else {
    42  			time.ResetTime()
    43  
    44  			success := coreStrToDate(proc.Ctx, time, functionUtil.QuickBytesToStr(v1), functionUtil.QuickBytesToStr(v2))
    45  			if success {
    46  				if types.ValidDate(int32(time.year), time.month, time.day) {
    47  					value := types.DateFromCalendar(int32(time.year), time.month, time.day)
    48  					if err := rs.Append(value, false); err != nil {
    49  						return err
    50  					}
    51  					continue
    52  				}
    53  			}
    54  			if err := rs.Append(0, true); err != nil {
    55  				return err
    56  			}
    57  		}
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  func builtInStrToDatetime(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error {
    64  	p1 := vector.GenerateFunctionStrParameter(parameters[0])
    65  	p2 := vector.GenerateFunctionStrParameter(parameters[1])
    66  
    67  	rs := vector.MustFunctionResult[types.Datetime](result)
    68  
    69  	time := NewGeneralTime()
    70  	for i := uint64(0); i < uint64(length); i++ {
    71  		v1, null1 := p1.GetStrValue(i)
    72  		v2, null2 := p2.GetStrValue(i)
    73  		if null1 || null2 {
    74  			if err := rs.Append(0, true); err != nil {
    75  				return err
    76  			}
    77  		} else {
    78  			time.ResetTime()
    79  
    80  			success := coreStrToDate(proc.Ctx, time, functionUtil.QuickBytesToStr(v1), functionUtil.QuickBytesToStr(v2))
    81  			if success {
    82  				if types.ValidDatetime(int32(time.year), time.month, time.day) && types.ValidTimeInDay(time.hour, time.minute, time.second) {
    83  					value := types.DatetimeFromClock(int32(time.year), time.month, time.day, time.hour, time.minute, time.second, time.microsecond)
    84  					if err := rs.Append(value, false); err != nil {
    85  						return err
    86  					}
    87  					continue
    88  				}
    89  			}
    90  			if err := rs.Append(0, true); err != nil {
    91  				return err
    92  			}
    93  		}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func builtInStrToTime(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error {
   100  	p1 := vector.GenerateFunctionStrParameter(parameters[0])
   101  	p2 := vector.GenerateFunctionStrParameter(parameters[1])
   102  
   103  	rs := vector.MustFunctionResult[types.Time](result)
   104  
   105  	time := NewGeneralTime()
   106  	for i := uint64(0); i < uint64(length); i++ {
   107  		v1, null1 := p1.GetStrValue(i)
   108  		v2, null2 := p2.GetStrValue(i)
   109  		if null1 || null2 {
   110  			if err := rs.Append(0, true); err != nil {
   111  				return err
   112  			}
   113  		} else {
   114  			time.ResetTime()
   115  
   116  			success := coreStrToDate(proc.Ctx, time, functionUtil.QuickBytesToStr(v1), functionUtil.QuickBytesToStr(v2))
   117  			if success {
   118  				if types.ValidTime(uint64(time.hour), uint64(time.minute), uint64(time.second)) {
   119  					value := types.TimeFromClock(false, uint64(time.hour), time.minute, time.second, time.microsecond)
   120  					if err := rs.Append(value, false); err != nil {
   121  						return err
   122  					}
   123  					continue
   124  				}
   125  			}
   126  			if err := rs.Append(0, true); err != nil {
   127  				return err
   128  			}
   129  		}
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func coreStrToDate(cctx context.Context, t *GeneralTime, date string, format string) bool {
   136  	ctx := make(map[string]int)
   137  	success := strToDate2(cctx, t, date, format, ctx)
   138  	if !success {
   139  		return false
   140  	}
   141  	if err := checkMysqlTime(cctx, t, ctx); err != nil {
   142  		return false
   143  	}
   144  	return true
   145  }
   146  
   147  // strToDate converts date string according to format,
   148  // the value will be stored in argument ctx. the second return value is true when success
   149  func strToDate2(cctx context.Context, t *GeneralTime, date string, format string, ctx map[string]int) (success bool) {
   150  	date = trimWhiteSpace(date)
   151  	format = trimWhiteSpace(format)
   152  
   153  	token, formatRemain, succ := nextFormatToken(format)
   154  	if !succ {
   155  		return false
   156  	}
   157  
   158  	if token == "" {
   159  		if len(date) != 0 {
   160  			// Extra characters at the end of date are ignored
   161  			return true
   162  		}
   163  		// Normal case. Both token and date are empty now.
   164  		return true
   165  	}
   166  
   167  	if len(date) == 0 {
   168  		ctx[token] = 0
   169  		return true
   170  	}
   171  
   172  	dateRemain, succ := matchDateWithToken(t, date, token, ctx)
   173  	if !succ {
   174  		return false
   175  	}
   176  
   177  	return strToDate2(cctx, t, dateRemain, formatRemain, ctx)
   178  }
   179  
   180  // checkMysqlTime fixes the Time use the values in the context.
   181  func checkMysqlTime(cctx context.Context, t *GeneralTime, ctx map[string]int) error {
   182  	if valueAMorPm, ok := ctx["%p"]; ok {
   183  		if _, ok := ctx["%H"]; ok {
   184  			return moerr.NewInternalError(cctx, "Truncated incorrect %-.64s value: '%-.128s'", "time", t)
   185  		}
   186  		if t.getHour() == 0 {
   187  			return moerr.NewInternalError(cctx, "Truncated incorrect %-.64s value: '%-.128s'", "time", t)
   188  		}
   189  		if t.getHour() == 12 {
   190  			// 12 is a special hour.
   191  			switch valueAMorPm {
   192  			case timeOfAM:
   193  				t.setHour(0)
   194  			case timeOfPM:
   195  				t.setHour(12)
   196  			}
   197  			return nil
   198  		}
   199  		if valueAMorPm == timeOfPM {
   200  			t.setHour(t.getHour() + 12)
   201  		}
   202  	} else {
   203  		if _, ok := ctx["%h"]; ok && t.getHour() == 12 {
   204  			t.setHour(0)
   205  		}
   206  	}
   207  	return nil
   208  }
   209  
   210  // trim spaces in strings
   211  func trimWhiteSpace(input string) string {
   212  	for i, c := range input {
   213  		if !unicode.IsSpace(c) {
   214  			return input[i:]
   215  		}
   216  	}
   217  	return ""
   218  }
   219  
   220  // nextFormatToken takes next one format control token from the string.
   221  // such as: format "%d %H %m" will get token "%d" and the remain is " %H %m".
   222  func nextFormatToken(format string) (token string, remain string, success bool) {
   223  	if len(format) == 0 {
   224  		return "", "", true
   225  	}
   226  
   227  	// Just one character.
   228  	if len(format) == 1 {
   229  		if format[0] == '%' {
   230  			return "", "", false
   231  		}
   232  		return format, "", true
   233  	}
   234  
   235  	// More than one character.
   236  	if format[0] == '%' {
   237  		return format[:2], format[2:], true
   238  	}
   239  
   240  	return format[:1], format[1:], true
   241  }