blob: b2ea0b4a4a852817f7a8a28d488548a3cc4a12ee [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package query_functions
// TODO(jkline): Probably rename this file to time_functions.go
import (
"time"
"v.io/syncbase/v23/syncbase/nosql/internal/query/conversions"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
"v.io/syncbase/v23/syncbase/nosql/query_db"
"v.io/syncbase/v23/syncbase/nosql/syncql"
)
// If possible, check if arg is convertible to a location. Fields and not yet computed
// functions cannot be checked and will just return nil.
func checkIfPossibleThatArgIsConvertibleToLocation(db query_db.Database, arg *query_parser.Operand) error {
var locStr *query_parser.Operand
var err error
switch arg.Type {
case query_parser.TypBigInt, query_parser.TypBigRat, query_parser.TypBool, query_parser.TypComplex, query_parser.TypFloat, query_parser.TypInt, query_parser.TypStr, query_parser.TypTime, query_parser.TypUint:
if locStr, err = conversions.ConvertValueToString(arg); err != nil {
if err != nil {
return syncql.NewErrLocationConversionError(db.GetContext(), arg.Off, err)
} else {
return nil
}
}
case query_parser.TypFunction:
if arg.Function.Computed {
if locStr, err = conversions.ConvertValueToString(arg.Function.RetValue); err != nil {
if err != nil {
return syncql.NewErrLocationConversionError(db.GetContext(), arg.Off, err)
} else {
return nil
}
}
} else {
// Arg is uncomputed function, can't make determination about arg.
return nil
}
default:
// Arg is not a literal or function, can't make determination about arg.
return nil
}
_, err = time.LoadLocation(locStr.Str)
if err != nil {
return syncql.NewErrLocationConversionError(db.GetContext(), arg.Off, err)
} else {
return nil
}
}
// Time(layout, value string)
// e.g., Time("Mon Jan 2 15:04:05 -0700 MST 2006", "Tue Aug 25 10:01:00 -0700 PDT 2015")
// e.g., Time("Jan 2 15:04 MST 2006", "Aug 25 10:01 PDT 2015")
func timeFunc(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
layoutOp, err := conversions.ConvertValueToString(args[0])
if err != nil {
return nil, err
}
valueOp, err := conversions.ConvertValueToString(args[1])
if err != nil {
return nil, err
}
// Mon Jan 2 15:04:05 -0700 MST 2006
tim, err := time.Parse(layoutOp.Str, valueOp.Str)
if err != nil {
return nil, err
}
return makeTimeOp(off, tim), nil
}
// now()
func now(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
return makeTimeOp(off, time.Now()), nil
}
func timeInLocation(db query_db.Database, off int64, args []*query_parser.Operand) (time.Time, error) {
var timeOp *query_parser.Operand
var locOp *query_parser.Operand
var err error
if timeOp, err = conversions.ConvertValueToTime(args[0]); err != nil {
return time.Time{}, err
}
if locOp, err = conversions.ConvertValueToString(args[1]); err != nil {
return time.Time{}, err
}
var loc *time.Location
if loc, err = time.LoadLocation(locOp.Str); err != nil {
return time.Time{}, err
}
return timeOp.Time.In(loc), nil
}
// Year(v.InvoiceDate, "America/Los_Angeles")
func year(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Year())), nil
}
}
// Month(v.InvoiceDate, "America/Los_Angeles")
func month(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Month())), nil
}
}
// Day(v.InvoiceDate, "America/Los_Angeles")
func day(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Day())), nil
}
}
// Hour(v.InvoiceDate, "America/Los_Angeles")
func hour(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Hour())), nil
}
}
// Minute(v.InvoiceDate, "America/Los_Angeles")
func minute(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Minute())), nil
}
}
// Second(v.InvoiceDate, "America/Los_Angeles")
func second(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Second())), nil
}
}
// Nanosecond(v.InvoiceDate, "America/Los_Angeles")
func nanosecond(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Nanosecond())), nil
}
}
// Weekday(v.InvoiceDate, "America/Los_Angeles")
func weekday(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.Weekday())), nil
}
}
// YearDay(v.InvoiceDate, "America/Los_Angeles")
func yearDay(db query_db.Database, off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
if tim, err := timeInLocation(db, off, args); err != nil {
return nil, err
} else {
return makeIntOp(off, int64(tim.YearDay())), nil
}
}
func makeTimeOp(off int64, tim time.Time) *query_parser.Operand {
var o query_parser.Operand
o.Off = off
o.Type = query_parser.TypTime
o.Time = tim
return &o
}
func secondArgLocationCheck(db query_db.Database, off int64, args []*query_parser.Operand) error {
// At this point, for the args that can be evaluated before execution, it is known that
// there are two args, a time followed by a string.
// Just need to check that the 2nd arg is convertible to a location.
if err := checkIfPossibleThatArgIsConvertibleToLocation(db, args[1]); err != nil {
return err
}
return nil
}