mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
fb-1893 Adding new scalar SQL function datetimeAdd(timeunit, duration, target) (#2295)
* fb-1893 Adding new scalar SQL function datetimeAdd(timeunit, duration, target)
This commit is contained in:
parent
eb6c6e3105
commit
0708673df5
6 changed files with 453 additions and 51 deletions
|
|
@ -1579,6 +1579,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er
|
|||
// time quantum functions
|
||||
case "RANGEQ":
|
||||
return n.EvaluateRangeQ(currentRow)
|
||||
case "DATETIMEADD":
|
||||
return n.EvaluateDatetimeAdd(currentRow)
|
||||
default:
|
||||
return nil, sql3.NewErrInternalf("unhandled function name '%s'", n.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(ctx context.Context, call *pars
|
|||
case "RANGEQ":
|
||||
return p.analyzeFunctionRangeQ(call, scope)
|
||||
|
||||
case "DATETIMEADD":
|
||||
return p.analyzeFunctionDatetimeAdd(call, scope)
|
||||
default:
|
||||
return nil, sql3.NewErrCallUnknownFunction(call.Name.NamePos.Line, call.Name.NamePos.Column, call.Name.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const intervalHour = "HH"
|
|||
const intervalMinute = "MI"
|
||||
const intervalSecond = "S"
|
||||
const intervalMillisecond = "MS"
|
||||
const intervalMicrosecond = "US"
|
||||
const intervalNanosecond = "NS"
|
||||
|
||||
func (p *ExecutionPlanner) analyzeFunctionDatePart(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
|
||||
|
|
@ -38,35 +39,66 @@ func (p *ExecutionPlanner) analyzeFunctionDatePart(call *parser.Call, scope pars
|
|||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[1].Pos().Line, call.Args[1].Pos().Column, call.Args[1].DataType().TypeDescription(), dateType.TypeDescription())
|
||||
}
|
||||
|
||||
//return int
|
||||
// return int
|
||||
call.ResultDataType = parser.NewDataTypeInt()
|
||||
|
||||
return call, nil
|
||||
}
|
||||
|
||||
func (p *ExecutionPlanner) analyzeFunctionToTimestamp(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
|
||||
//param1 is the number to be converted to timestamp. This param is required.
|
||||
//param2 is the time unit of the numeric value in param 1. This param is optional.
|
||||
//ToTimestamp can be invoked with just param1.
|
||||
// param1 is the number to be converted to timestamp. This param is required.
|
||||
// param2 is the time unit of the numeric value in param 1. This param is optional.
|
||||
// ToTimestamp can be invoked with just param1.
|
||||
if len(call.Args) != 1 && len(call.Args) != 2 {
|
||||
return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 2, len(call.Args))
|
||||
}
|
||||
|
||||
//param1 is a integer of type int64
|
||||
// param1 is a integer of type int64
|
||||
param1Type := parser.NewDataTypeInt()
|
||||
if !typesAreAssignmentCompatible(param1Type, call.Args[0].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[0].Pos().Line, call.Args[0].Pos().Column, call.Args[0].DataType().TypeDescription(), param1Type.TypeDescription())
|
||||
}
|
||||
|
||||
//param2 is a string and it should be one of 's', 'ms', 'us', 'ns'.
|
||||
//param2 is optional, will be defaulted to 's' if not supplied.
|
||||
// param2 is a string and it should be one of 's', 'ms', 'us', 'ns'.
|
||||
// param2 is optional, will be defaulted to 's' if not supplied.
|
||||
if len(call.Args) == 2 {
|
||||
param2Type := parser.NewDataTypeString()
|
||||
if !typesAreAssignmentCompatible(param2Type, call.Args[1].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[1].Pos().Line, call.Args[1].Pos().Column, call.Args[1].DataType().TypeDescription(), param2Type.TypeDescription())
|
||||
}
|
||||
}
|
||||
//ToTimestamp returns a timestamp calculated from param1 using time unit passed in param 2
|
||||
// ToTimestamp returns a timestamp calculated from param1 using time unit passed in param 2
|
||||
call.ResultDataType = parser.NewDataTypeTimestamp()
|
||||
return call, nil
|
||||
}
|
||||
|
||||
func (p *ExecutionPlanner) analyzeFunctionDatetimeAdd(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
|
||||
// param1 is the time unit of duration to be added to the target timestamp.
|
||||
// param2 is the time duration to be added to the target timestamp.
|
||||
// param3 is the target timestamp to which the time duration to be added.
|
||||
if len(call.Args) != 3 {
|
||||
return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 3, len(call.Args))
|
||||
}
|
||||
|
||||
// param1- time unit is a string and it should be one of 'yy','m','d','hh','mi','s', 'ms', 'us', 'ns'.
|
||||
param1Type := parser.NewDataTypeString()
|
||||
if !typesAreAssignmentCompatible(param1Type, call.Args[0].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[0].Pos().Line, call.Args[0].Pos().Column, call.Args[0].DataType().TypeDescription(), param1Type.TypeDescription())
|
||||
}
|
||||
|
||||
// param2- time duration is a int
|
||||
param2Type := parser.NewDataTypeInt()
|
||||
if !typesAreAssignmentCompatible(param2Type, call.Args[1].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[1].Pos().Line, call.Args[1].Pos().Column, call.Args[1].DataType().TypeDescription(), param2Type.TypeDescription())
|
||||
}
|
||||
|
||||
// param3- target datetime to which the duration to be added to
|
||||
param3Type := parser.NewDataTypeTimestamp()
|
||||
if !typesAreAssignmentCompatible(param3Type, call.Args[2].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[2].Pos().Line, call.Args[2].Pos().Column, call.Args[2].DataType().TypeDescription(), param3Type.TypeDescription())
|
||||
}
|
||||
|
||||
// DatetimeAdd returns a timestamp calculated by adding param2 to param3 using time unit passed in param 1
|
||||
call.ResultDataType = parser.NewDataTypeTimestamp()
|
||||
return call, nil
|
||||
}
|
||||
|
|
@ -87,7 +119,7 @@ func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interfa
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
//get the date value
|
||||
// get the date value
|
||||
coercedDate, err := coerceValue(n.args[1].Type(), parser.NewDataTypeTimestamp(), dateEval, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -98,7 +130,7 @@ func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interfa
|
|||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
|
||||
//get the interval value
|
||||
// get the interval value
|
||||
coercedInterval, err := coerceValue(n.args[0].Type(), parser.NewDataTypeString(), intervalEval, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -139,10 +171,13 @@ func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interfa
|
|||
return int64(date.Second()), nil
|
||||
|
||||
case intervalMillisecond:
|
||||
return int64(date.Nanosecond() * 1000 * 1000), nil
|
||||
return int64(date.Nanosecond() / 1000000), nil
|
||||
|
||||
case intervalMicrosecond:
|
||||
return int64((date.Nanosecond() % 1000000) / 1000), nil
|
||||
|
||||
case intervalNanosecond:
|
||||
return int64(date.Nanosecond()), nil
|
||||
return int64(date.Nanosecond() % 1000), nil
|
||||
|
||||
default:
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, interval, "interval")
|
||||
|
|
@ -151,48 +186,147 @@ func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interfa
|
|||
}
|
||||
|
||||
func (n *callPlanExpression) EvaluateToTimestamp(currentRow []interface{}) (interface{}, error) {
|
||||
//retrieve param1, the number to be converted to timestamp
|
||||
// retrieve param1, the number to be converted to timestamp
|
||||
param1, err := n.args[0].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if param1 == nil {
|
||||
//if the param1 is null silently return null timestamp value
|
||||
// if the param1 is null silently return null timestamp value
|
||||
return nil, nil
|
||||
}
|
||||
coercedParam1, err := coerceValue(n.args[0].Type(), parser.NewDataTypeInt(), param1, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
//raise error if param 1 is not an integer. Should we return nil instead of raising error here? see note at return.
|
||||
// raise error if param 1 is not an integer. Should we return nil instead of raising error here? see note at return.
|
||||
return nil, err
|
||||
}
|
||||
num, ok := coercedParam1.(int64)
|
||||
if !ok {
|
||||
//raise error if param 1 is not an integer. Should we return nil instead of raising error here? see note at return.
|
||||
// raise error if param 1 is not an integer. Should we return nil instead of raising error here? see note at return.
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
|
||||
//retrieve param2, time unit for param1, if not supplied default to seconds 's'.
|
||||
// retrieve param2, time unit for param1, if not supplied default to seconds 's'.
|
||||
var unit string = featurebase.TimeUnitSeconds
|
||||
if len(n.args) == 2 {
|
||||
param2, err := n.args[1].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
//raise error if unable to retieve the argument for param2
|
||||
// raise error if unable to retieve the argument for param2
|
||||
return nil, err
|
||||
}
|
||||
coercedParam2, err := coerceValue(n.args[1].Type(), parser.NewDataTypeString(), param2, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
//raise error if param2 is not a string
|
||||
// raise error if param2 is not a string
|
||||
return nil, err
|
||||
}
|
||||
unit, ok = coercedParam2.(string)
|
||||
if !ok {
|
||||
//raise error if param2 is not a string
|
||||
// raise error if param2 is not a string
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
if !featurebase.IsValidTimeUnit(unit) {
|
||||
//raise error is param2 is not a valid time unit
|
||||
// raise error is param2 is not a valid time unit
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, unit, "timeunit")
|
||||
}
|
||||
}
|
||||
//should we throw error or return nil if the conversion fails? what is the desired behaviour when ToTimestamp errors for one bad record in a batch of thousands?
|
||||
// should we throw error or return nil if the conversion fails? what is the desired behaviour when ToTimestamp errors for one bad record in a batch of thousands?
|
||||
return featurebase.ValToTimestamp(unit, num)
|
||||
}
|
||||
|
||||
func (n *callPlanExpression) EvaluateDatetimeAdd(currentRow []interface{}) (interface{}, error) {
|
||||
// retrieve param1, timeunit of the value to be added to the target timestamp
|
||||
param1, err := n.args[0].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coercedParam1, err := coerceValue(n.args[0].Type(), parser.NewDataTypeString(), param1, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
// raise error if param 1 is not string.
|
||||
return nil, err
|
||||
}
|
||||
timeunit, ok := coercedParam1.(string)
|
||||
if !ok {
|
||||
// raise error if param 1 is not string.
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
|
||||
// retrieve param2, timeduration to be added to the target timestamp.
|
||||
param2, err := n.args[1].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
// raise error if unable to retieve the argument for param2
|
||||
return nil, err
|
||||
}
|
||||
// retrieve param3, target timestamp to which the timeduration to be added to.
|
||||
param3, err := n.args[2].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
// raise error if unable to retieve the argument for param3
|
||||
return nil, err
|
||||
}
|
||||
if param2 == nil || param3 == nil {
|
||||
// if either of timeduration or target datetime is null then return null
|
||||
return nil, nil
|
||||
}
|
||||
coercedParam2, err := coerceValue(n.args[1].Type(), parser.NewDataTypeInt(), param2, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
// raise error if param2 is not a string
|
||||
return nil, err
|
||||
}
|
||||
timeduration, ok := coercedParam2.(int64)
|
||||
if !ok {
|
||||
// raise error if param2 is not a integer
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
coercedParam3, err := coerceValue(n.args[2].Type(), parser.NewDataTypeTimestamp(), param3, parser.Pos{Line: 0, Column: 0})
|
||||
if err != nil {
|
||||
// raise error if param3 is not a timestamp
|
||||
return nil, err
|
||||
}
|
||||
target, ok := coercedParam3.(time.Time)
|
||||
if !ok {
|
||||
// raise error if param3 is not a datetime
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
if !isValidTimeInterval(strings.ToUpper(timeunit)) {
|
||||
// raise error if timeunit value is invalid
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, timeunit, "timeunit")
|
||||
} else if target.IsZero() {
|
||||
// return nil if target is nil
|
||||
return nil, nil
|
||||
} else if timeduration == 0 {
|
||||
// return target if duration to add is 0
|
||||
return target, nil
|
||||
}
|
||||
switch strings.ToUpper(timeunit) {
|
||||
case intervalYear:
|
||||
return target.AddDate(int(timeduration), 0, 0), nil
|
||||
case intervalMonth:
|
||||
return target.AddDate(0, int(timeduration), 0), nil
|
||||
case intervalDay:
|
||||
return target.AddDate(0, 0, int(timeduration)), nil
|
||||
case intervalHour:
|
||||
return target.Add(time.Hour * time.Duration(timeduration)), nil
|
||||
case intervalMinute:
|
||||
return target.Add(time.Minute * time.Duration(timeduration)), nil
|
||||
case intervalSecond:
|
||||
return target.Add(time.Second * time.Duration(timeduration)), nil
|
||||
case intervalMillisecond:
|
||||
return target.Add(time.Millisecond * time.Duration(timeduration)), nil
|
||||
case intervalMicrosecond:
|
||||
return target.Add(time.Microsecond * time.Duration(timeduration)), nil
|
||||
case intervalNanosecond:
|
||||
return target.Add(time.Nanosecond * time.Duration(timeduration)), nil
|
||||
default:
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, timeunit, "timeunit")
|
||||
}
|
||||
}
|
||||
|
||||
// isValidTimeInterval returns true if part is valid.
|
||||
func isValidTimeInterval(unit string) bool {
|
||||
switch unit {
|
||||
case intervalYear, intervalYearDay, intervalMonth, intervalDay, intervalWeeKDay,
|
||||
intervalWeek, intervalHour, intervalMinute, intervalSecond, intervalMillisecond,
|
||||
intervalMicrosecond, intervalNanosecond:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ var TableTests []TableTest = []TableTest{
|
|||
setFunctionTests,
|
||||
setParameterTests,
|
||||
datePartTests,
|
||||
toTimestampTests,
|
||||
datetimeAddTests,
|
||||
stringScalarFunctionsTests,
|
||||
|
||||
insertTest,
|
||||
|
|
@ -210,6 +212,16 @@ func knownTimestamp() time.Time {
|
|||
return tm
|
||||
}
|
||||
|
||||
func knownSubSecondTimestamp() time.Time {
|
||||
tm := knownTimestamp()
|
||||
duration, err := time.ParseDuration("100200300ns")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
tm = tm.Add(duration)
|
||||
return tm
|
||||
}
|
||||
|
||||
func timestampFromString(s string) time.Time {
|
||||
tm, err := time.ParseInLocation(time.RFC3339, s, time.UTC)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ var datePartTests = TableTest{
|
|||
srcHdr("_id", fldTypeID),
|
||||
srcHdr("a", fldTypeInt, "min 0", "max 1000"),
|
||||
srcHdr("b", fldTypeInt, "min 0", "max 1000"),
|
||||
srcHdr("ts", fldTypeTimestamp),
|
||||
srcHdr("ts", fldTypeTimestamp, "timeunit 'ns'"),
|
||||
),
|
||||
srcRows(
|
||||
srcRow(int64(1), int64(10), int64(100), knownTimestamp()),
|
||||
srcRow(int64(1), int64(10), int64(100), knownSubSecondTimestamp()),
|
||||
),
|
||||
),
|
||||
SQLTests: []SQLTest{
|
||||
|
|
@ -36,30 +36,6 @@ var datePartTests = TableTest{
|
|||
),
|
||||
ExpErr: "invalid value '1' for parameter 'interval'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp()",
|
||||
),
|
||||
ExpErr: "count of formal parameters (2) does not match count of actual parameters (0)",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp('a')",
|
||||
),
|
||||
ExpErr: "an expression of type 'string' cannot be passed to a parameter of type 'int'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp(1, 2)",
|
||||
),
|
||||
ExpErr: "an expression of type 'int' cannot be passed to a parameter of type 'string'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp(1, 'x')",
|
||||
),
|
||||
ExpErr: "invalid value 'x' for parameter 'timeunit'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('yy', ts) from dateparttests",
|
||||
|
|
@ -186,7 +162,20 @@ var datePartTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(0)),
|
||||
row(int64(1), int64(100)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('us', ts) from dateparttests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(200)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -199,7 +188,7 @@ var datePartTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(0)),
|
||||
row(int64(1), int64(300)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -218,6 +207,42 @@ var datePartTests = TableTest{
|
|||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// toTimestamp tests
|
||||
var toTimestampTests = TableTest{
|
||||
|
||||
Table: tbl(
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
SQLTests: []SQLTest{
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp()",
|
||||
),
|
||||
ExpErr: "count of formal parameters (2) does not match count of actual parameters (0)",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp('a')",
|
||||
),
|
||||
ExpErr: "an expression of type 'string' cannot be passed to a parameter of type 'int'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp(1, 2)",
|
||||
),
|
||||
ExpErr: "an expression of type 'int' cannot be passed to a parameter of type 'string'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select totimestamp(1, 'x')",
|
||||
),
|
||||
ExpErr: "invalid value 'x' for parameter 'timeunit'",
|
||||
},
|
||||
{
|
||||
//test ToTimestamp(num, timeunit) for all possible time unit values
|
||||
SQLs: sqls(
|
||||
|
|
@ -243,3 +268,230 @@ var datePartTests = TableTest{
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
// datetimeAdd tests
|
||||
var datetimeAddTests = TableTest{
|
||||
|
||||
Table: tbl(
|
||||
"datetimeadd",
|
||||
srcHdrs(
|
||||
srcHdr("_id", fldTypeID),
|
||||
srcHdr("ts", fldTypeTimestamp, "timeunit 'ns'"),
|
||||
),
|
||||
srcRows(
|
||||
srcRow(int64(1), knownSubSecondTimestamp()),
|
||||
),
|
||||
),
|
||||
SQLTests: []SQLTest{
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd()",
|
||||
),
|
||||
ExpErr: "count of formal parameters (3) does not match count of actual parameters (0)",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd(1,1,current_timestamp)",
|
||||
),
|
||||
ExpErr: "an expression of type 'int' cannot be passed to a parameter of type 'string'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd('yy', '2',current_timestamp)",
|
||||
),
|
||||
ExpErr: "an expression of type 'string' cannot be passed to a parameter of type 'int'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd('yy', 2, true)",
|
||||
),
|
||||
ExpErr: "an expression of type 'bool' cannot be passed to a parameter of type 'timestamp'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd('x',1,current_timestamp)",
|
||||
),
|
||||
ExpErr: "invalid value 'x' for parameter 'timeunit'",
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select datetimeadd('ms',7000,'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
ExpErr: "unable to convert 'YYYY-MM-DDTHH:MM:SS' to type 'timestamp'",
|
||||
},
|
||||
//Test datetimeadd() for all possible time units
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('YY',datetimeadd('YY', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(2013)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('M',datetimeadd('M', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(12)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('D',datetimeadd('D', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(2)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('HH',datetimeadd('HH', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(23)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('MI',datetimeadd('MI', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(9)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('S',datetimeadd('S', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(42)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('MS',datetimeadd('MS', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(101)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('US',datetimeadd('US', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(201)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('NS',datetimeadd('NS', 1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(301)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
//test datetimeadd() for subtraction
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('YY',datetimeadd('YY', -1, ts)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(2011)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
//test datetimeadd() for transition
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('NS',datetimeadd('NS', 700, ts)) as a, datepart('US',datetimeadd('NS', 700, ts)) as b from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("a", fldTypeInt),
|
||||
hdr("b", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(0), int64(201)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
//test datetimeadd() for literals
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('YY',datetimeadd('YY', 1, 0)) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(1971)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datepart('YY',datetimeadd('YY', 1, '2023-03-03T00:00:00Z')) from datetimeadd",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(2024)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ func (sr sourceRows) insertTuples(t *testing.T) string {
|
|||
case nil:
|
||||
sb.WriteString("null")
|
||||
case time.Time:
|
||||
sb.WriteString("'" + v.Format(time.RFC3339) + "'")
|
||||
sb.WriteString("'" + v.Format(time.RFC3339Nano) + "'")
|
||||
|
||||
default:
|
||||
t.Fatalf("unsupported cell type: %T", cell)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue