mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Added date_trunc time/date scalar function (#2312)
FB-1961 Added function and test coverage.
This commit is contained in:
parent
ef14f3a560
commit
6de130fe39
5 changed files with 266 additions and 0 deletions
|
|
@ -1581,6 +1581,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er
|
|||
return n.EvaluateStr(currentRow)
|
||||
case "DATETIMENAME":
|
||||
return n.EvaluateDateTimeName(currentRow)
|
||||
case "DATE_TRUNC":
|
||||
return n.EvaluateDateTrunc(currentRow)
|
||||
// time quantum functions
|
||||
case "RANGEQ":
|
||||
return n.EvaluateRangeQ(currentRow)
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(ctx context.Context, call *pars
|
|||
return p.analyseFunctionStr(call, scope)
|
||||
case "DATETIMENAME":
|
||||
return p.analyzeFunctionDateTimeName(call, scope)
|
||||
case "DATE_TRUNC":
|
||||
return p.analyzeFunctionDateTrunc(call, scope)
|
||||
// time quantum funtions
|
||||
case "RANGEQ":
|
||||
return p.analyzeFunctionRangeQ(call, scope)
|
||||
|
|
|
|||
|
|
@ -144,6 +144,28 @@ func (p *ExecutionPlanner) analyzeFunctionDateTimeName(call *parser.Call, scope
|
|||
return call, nil
|
||||
}
|
||||
|
||||
func (p *ExecutionPlanner) analyzeFunctionDateTrunc(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
|
||||
|
||||
if len(call.Args) != 2 {
|
||||
return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 2, len(call.Args))
|
||||
}
|
||||
// interval
|
||||
intervalType := parser.NewDataTypeString()
|
||||
if !typesAreAssignmentCompatible(intervalType, call.Args[0].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[0].Pos().Line, call.Args[0].Pos().Column, call.Args[0].DataType().TypeDescription(), intervalType.TypeDescription())
|
||||
}
|
||||
|
||||
// date
|
||||
dateType := parser.NewDataTypeTimestamp()
|
||||
if !typesAreAssignmentCompatible(dateType, call.Args[1].DataType()) {
|
||||
return nil, sql3.NewErrParameterTypeMistmatch(call.Args[1].Pos().Line, call.Args[1].Pos().Column, call.Args[1].DataType().TypeDescription(), dateType.TypeDescription())
|
||||
}
|
||||
|
||||
//return int
|
||||
call.ResultDataType = parser.NewDataTypeString()
|
||||
return call, nil
|
||||
}
|
||||
|
||||
// analyzeFunctionDateTimeDiff ensures a timeunit and start and end timestamps.
|
||||
func (p *ExecutionPlanner) analyzeFunctionDateTimeDiff(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
|
||||
if len(call.Args) != 3 {
|
||||
|
|
@ -539,6 +561,67 @@ func (n *callPlanExpression) EvaluateDatetimeAdd(currentRow []interface{}) (inte
|
|||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, timeunit, "timeunit")
|
||||
}
|
||||
}
|
||||
func (n *callPlanExpression) EvaluateDateTrunc(currentRow []interface{}) (interface{}, error) {
|
||||
intervalEval, err := n.args[0].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dateEval, err := n.args[1].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// nil if anything is nil
|
||||
if intervalEval == nil || dateEval == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
date, dateOk := coercedDate.(time.Time)
|
||||
if !dateOk {
|
||||
return nil, sql3.NewErrInternalf("unable to convert 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
|
||||
}
|
||||
|
||||
interval, intervalOk := coercedInterval.(string)
|
||||
if !intervalOk {
|
||||
return nil, sql3.NewErrInternalf("unable to convert value")
|
||||
}
|
||||
|
||||
switch strings.ToUpper(interval) {
|
||||
case intervalYear:
|
||||
return date.Format("2006"), nil
|
||||
case intervalMonth:
|
||||
return date.Format("2006-01"), nil
|
||||
case intervalDay:
|
||||
return date.Format("2006-01-02"), nil
|
||||
case intervalHour:
|
||||
return date.Format("2006-01-02T15"), nil
|
||||
case intervalMinute:
|
||||
return date.Format("2006-01-02T15:04"), nil
|
||||
case intervalSecond:
|
||||
return date.Format("2006-01-02T15:04:05"), nil
|
||||
case intervalMillisecond:
|
||||
return date.Format("2006-01-02T15:04:05.000"), nil
|
||||
case intervalMicrosecond:
|
||||
return date.Format("2006-01-02T15:04:05.000000"), nil
|
||||
case intervalNanosecond:
|
||||
return date.Format("2006-01-02T15:04:05.000000000"), nil
|
||||
default:
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, interval, "interval")
|
||||
}
|
||||
}
|
||||
|
||||
// isValidTimeInterval returns true if part is valid.
|
||||
func isValidTimeInterval(unit string) bool {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ var TableTests []TableTest = []TableTest{
|
|||
dateTimeNameTests,
|
||||
toTimestampTests,
|
||||
datetimeAddTests,
|
||||
dateTruncTests,
|
||||
datetimedifftests,
|
||||
|
||||
stringScalarFunctionsTests,
|
||||
|
|
|
|||
|
|
@ -659,6 +659,184 @@ var datetimeAddTests = TableTest{
|
|||
},
|
||||
}
|
||||
|
||||
var dateTruncTests = TableTest{
|
||||
|
||||
Table: tbl(
|
||||
"datetrunctests",
|
||||
srcHdrs(
|
||||
srcHdr("_id", fldTypeID),
|
||||
srcHdr("ts", fldTypeTimestamp, "timeunit 'ns'"),
|
||||
),
|
||||
srcRows(
|
||||
srcRow(int64(1), knownSubSecondTimestamp()),
|
||||
),
|
||||
),
|
||||
|
||||
SQLTests: []SQLTest{
|
||||
{
|
||||
name: "DateTruncIncorrectParamsCount",
|
||||
SQLs: sqls(
|
||||
"select date_trunc()",
|
||||
),
|
||||
ExpErr: "count of formal parameters (2) does not match count of actual parameters (0)",
|
||||
},
|
||||
{
|
||||
name: "DateTruncTypeError",
|
||||
SQLs: sqls(
|
||||
"select date_trunc(1, 2)",
|
||||
),
|
||||
ExpErr: "an expression of type 'int' cannot be passed to a parameter of type 'string'",
|
||||
},
|
||||
{
|
||||
name: "DateTruncInvalidParam",
|
||||
SQLs: sqls(
|
||||
"select date_trunc('1', current_timestamp)",
|
||||
),
|
||||
ExpErr: "invalid value '1' for parameter 'interval'",
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnYear",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('yy', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnMonth",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('m', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnDay",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('d', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnHour",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('hh', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnMinute",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('mi', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22:08"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnSecond",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('s', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22:08:41"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnMilliS",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('ms', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22:08:41.100"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnMicroS",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('us', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22:08:41.100200"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "DateTruncOnNanoS",
|
||||
SQLs: sqls(
|
||||
"select _id, date_trunc('ns', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012-11-01T22:08:41.100200300"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
{
|
||||
name: "VerifyTimeStamp",
|
||||
SQLs: sqls(
|
||||
"select _id, datetimename('ns', ts) from datetrunctests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "100200300"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var datetimedifftests = TableTest{
|
||||
name: "DatetimeDiff",
|
||||
Table: tbl("dttable",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue