mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-10 07:01:01 +00:00
Add datetimename function to SQL3 (#2293)
FB-1896
Added the datetimename function, which returns parts of a timestamp
as strings. Month and day of the week are named ("January", "Monday")
while others are returned as a string of digits ("2023").
Added tests to the test definitions.
This commit is contained in:
parent
05ebdd15f0
commit
7ea4135ecf
5 changed files with 176 additions and 7 deletions
|
|
@ -1576,6 +1576,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er
|
|||
return n.EvaluateToTimestamp(currentRow)
|
||||
case "STR":
|
||||
return n.EvaluateStr(currentRow)
|
||||
case "DATETIMENAME":
|
||||
return n.EvaluateDateTimeName(currentRow)
|
||||
// time quantum functions
|
||||
case "RANGEQ":
|
||||
return n.EvaluateRangeQ(currentRow)
|
||||
|
|
|
|||
|
|
@ -256,6 +256,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(ctx context.Context, call *pars
|
|||
return p.analyzeFunctionToTimestamp(call, scope)
|
||||
case "STR":
|
||||
return p.analyseFunctionStr(call, scope)
|
||||
case "DATETIMENAME":
|
||||
return p.analyzeFunctionDateTimeName(call, scope)
|
||||
// time quantum funtions
|
||||
case "RANGEQ":
|
||||
return p.analyzeFunctionRangeQ(call, scope)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package planner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -103,6 +104,29 @@ func (p *ExecutionPlanner) analyzeFunctionDatetimeAdd(call *parser.Call, scope p
|
|||
return call, nil
|
||||
}
|
||||
|
||||
func (p *ExecutionPlanner) analyzeFunctionDateTimeName(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
|
||||
}
|
||||
|
||||
func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interface{}, error) {
|
||||
intervalEval, err := n.args[0].Evaluate(currentRow)
|
||||
if err != nil {
|
||||
|
|
@ -174,10 +198,10 @@ func (n *callPlanExpression) EvaluateDatepart(currentRow []interface{}) (interfa
|
|||
return int64(date.Nanosecond() / 1000000), nil
|
||||
|
||||
case intervalMicrosecond:
|
||||
return int64((date.Nanosecond() % 1000000) / 1000), nil
|
||||
return int64(date.Nanosecond() / 1000), nil
|
||||
|
||||
case intervalNanosecond:
|
||||
return int64(date.Nanosecond() % 1000), nil
|
||||
return int64(date.Nanosecond()), nil
|
||||
|
||||
default:
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, interval, "interval")
|
||||
|
|
@ -232,6 +256,85 @@ func (n *callPlanExpression) EvaluateToTimestamp(currentRow []interface{}) (inte
|
|||
return featurebase.ValToTimestamp(unit, num)
|
||||
}
|
||||
|
||||
func (n *callPlanExpression) EvaluateDateTimeName(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 fmt.Sprint(date.Year()), nil
|
||||
|
||||
case intervalYearDay:
|
||||
return fmt.Sprint(date.YearDay()), nil
|
||||
|
||||
case intervalMonth:
|
||||
return fmt.Sprint(date.Month()), nil
|
||||
|
||||
case intervalDay:
|
||||
return fmt.Sprint(date.Day()), nil
|
||||
|
||||
case intervalWeeKDay:
|
||||
return fmt.Sprint(date.Weekday()), nil
|
||||
|
||||
case intervalWeek:
|
||||
_, isoWeek := date.ISOWeek()
|
||||
return fmt.Sprint(isoWeek), nil
|
||||
|
||||
case intervalHour:
|
||||
return fmt.Sprint(date.Hour()), nil
|
||||
|
||||
case intervalMinute:
|
||||
return fmt.Sprint(date.Minute()), nil
|
||||
|
||||
case intervalSecond:
|
||||
return fmt.Sprint(date.Second()), nil
|
||||
|
||||
case intervalMillisecond:
|
||||
return fmt.Sprint(date.Nanosecond() / 1000000), nil
|
||||
case intervalMicrosecond:
|
||||
return fmt.Sprint(date.Nanosecond() / 1000), nil
|
||||
case intervalNanosecond:
|
||||
return fmt.Sprint(date.Nanosecond()), nil
|
||||
|
||||
default:
|
||||
return nil, sql3.NewErrCallParameterValueInvalid(0, 0, interval, "interval")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -46,8 +46,10 @@ var TableTests []TableTest = []TableTest{
|
|||
setFunctionTests,
|
||||
setParameterTests,
|
||||
datePartTests,
|
||||
dateTimeNameTests,
|
||||
toTimestampTests,
|
||||
datetimeAddTests,
|
||||
|
||||
stringScalarFunctionsTests,
|
||||
|
||||
insertTest,
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ var datePartTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(200)),
|
||||
row(int64(1), int64(100200)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -188,7 +188,7 @@ var datePartTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(300)),
|
||||
row(int64(1), int64(100200300)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -269,6 +269,66 @@ var toTimestampTests = TableTest{
|
|||
},
|
||||
}
|
||||
|
||||
var dateTimeNameTests = TableTest{
|
||||
|
||||
Table: tbl(
|
||||
"datetimenametests",
|
||||
srcHdrs(
|
||||
srcHdr("_id", fldTypeID),
|
||||
srcHdr("ts", fldTypeTimestamp),
|
||||
),
|
||||
srcRows(
|
||||
srcRow(int64(1), knownTimestamp()),
|
||||
),
|
||||
),
|
||||
SQLTests: []SQLTest{
|
||||
// dateTimeName tests
|
||||
// make sure returning a string as the year still works
|
||||
// if this works then the other parts converted to strings of digits should also work
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datetimename('yy', ts) from dateparttests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "2012"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
//check to make sure it gets a month name correctly
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datetimename('m', ts) from dateparttests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "November"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
// check to make sure it gets a day of the week correctly
|
||||
{
|
||||
SQLs: sqls(
|
||||
"select _id, datetimename('w', ts) from dateparttests",
|
||||
),
|
||||
ExpHdrs: hdrs(
|
||||
hdr("_id", fldTypeID),
|
||||
hdr("", fldTypeString),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), "Thursday"),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// datetimeAdd tests
|
||||
var datetimeAddTests = TableTest{
|
||||
|
||||
|
|
@ -420,7 +480,7 @@ var datetimeAddTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(201)),
|
||||
row(int64(1), int64(100201)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -433,7 +493,7 @@ var datetimeAddTests = TableTest{
|
|||
hdr("", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(301)),
|
||||
row(int64(1), int64(100200301)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
@ -462,7 +522,7 @@ var datetimeAddTests = TableTest{
|
|||
hdr("b", fldTypeInt),
|
||||
),
|
||||
ExpRows: rows(
|
||||
row(int64(1), int64(0), int64(201)),
|
||||
row(int64(1), int64(100201000), int64(100201)),
|
||||
),
|
||||
Compare: CompareExactUnordered,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue