Skip to content

Commit

Permalink
Merge branch 'main' into add-scheduled-report-dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
ericpgreen2 committed Nov 7, 2023
2 parents dc02059 + cc61866 commit 7c36e26
Show file tree
Hide file tree
Showing 57 changed files with 1,878 additions and 955 deletions.
2 changes: 1 addition & 1 deletion runtime/queries/metricsview_aggregation.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (q *MetricsViewAggregation) buildMetricsAggregationSQL(mv *runtimev1.Metric
whereClause := ""
if mv.TimeDimension != "" {
timeCol := safeName(mv.TimeDimension)
clause, err := timeRangeClause(q.TimeRange, dialect, timeCol, &args)
clause, err := timeRangeClause(q.TimeRange, mv, dialect, timeCol, &args)
if err != nil {
return "", nil, err
}
Expand Down
61 changes: 7 additions & 54 deletions runtime/queries/metricsview_comparison_toplist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import (
"fmt"
"io"
"strings"
"time"

runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
"github.com/rilldata/rill/runtime"
"github.com/rilldata/rill/runtime/drivers"
"github.com/rilldata/rill/runtime/pkg/duration"
"github.com/rilldata/rill/runtime/pkg/pbutil"
"google.golang.org/protobuf/types/known/structpb"

Expand Down Expand Up @@ -270,7 +268,7 @@ func (q *MetricsViewComparison) buildMetricsTopListSQL(mv *runtimev1.MetricsView
args := []any{}
td := safeName(mv.TimeDimension)

trc, err := timeRangeClause(q.TimeRange, dialect, td, &args)
trc, err := timeRangeClause(q.TimeRange, mv, dialect, td, &args)
if err != nil {
return "", nil, err
}
Expand Down Expand Up @@ -414,7 +412,7 @@ func (q *MetricsViewComparison) buildMetricsComparisonTopListSQL(mv *runtimev1.M

td := safeName(mv.TimeDimension)

trc, err := timeRangeClause(q.TimeRange, dialect, td, &args)
trc, err := timeRangeClause(q.TimeRange, mv, dialect, td, &args)
if err != nil {
return "", nil, err
}
Expand All @@ -430,7 +428,7 @@ func (q *MetricsViewComparison) buildMetricsComparisonTopListSQL(mv *runtimev1.M
args = append(args, clauseArgs...)
}

trc, err = timeRangeClause(q.ComparisonTimeRange, dialect, td, &args)
trc, err = timeRangeClause(q.ComparisonTimeRange, mv, dialect, td, &args)
if err != nil {
return "", nil, err
}
Expand Down Expand Up @@ -835,60 +833,15 @@ func (q *MetricsViewComparison) generateFilename() string {

// TODO: a) Ensure correct time zone handling, b) Implement support for tr.RoundToGrain
// (Maybe consider pushing all this logic into the SQL instead?)
func timeRangeClause(tr *runtimev1.TimeRange, dialect drivers.Dialect, timeCol string, args *[]any) (string, error) {
func timeRangeClause(tr *runtimev1.TimeRange, mv *runtimev1.MetricsViewSpec, dialect drivers.Dialect, timeCol string, args *[]any) (string, error) {
var clause string
if isTimeRangeNil(tr) {
return clause, nil
}

tz := time.UTC
if tr.TimeZone != "" {
var err error
tz, err = time.LoadLocation(tr.TimeZone)
if err != nil {
return "", fmt.Errorf("invalid time_range.time_zone %q: %w", tr.TimeZone, err)
}
}

var start, end time.Time
if tr.Start != nil {
start = tr.Start.AsTime().In(tz)
}
if tr.End != nil {
end = tr.End.AsTime().In(tz)
}

if tr.IsoDuration != "" {
if !start.IsZero() && !end.IsZero() {
return "", fmt.Errorf("only two of time_range.{start,end,iso_duration} can be specified")
}

d, err := duration.ParseISO8601(tr.IsoDuration)
if err != nil {
return "", fmt.Errorf("invalid iso_duration %q: %w", tr.IsoDuration, err)
}

if !start.IsZero() {
end = d.Add(start)
} else if !end.IsZero() {
start = d.Sub(end)
} else {
return "", fmt.Errorf("one of time_range.{start,end} must be specified with time_range.iso_duration")
}
}

if tr.IsoOffset != "" {
d, err := duration.ParseISO8601(tr.IsoOffset)
if err != nil {
return "", fmt.Errorf("invalid iso_offset %q: %w", tr.IsoOffset, err)
}

if !start.IsZero() {
start = d.Add(start)
}
if !end.IsZero() {
end = d.Add(end)
}
start, end, err := ResolveTimeRange(tr, mv)
if err != nil {
return "", err
}

if !start.IsZero() {
Expand Down
61 changes: 0 additions & 61 deletions runtime/queries/metricsview_timeseries.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,67 +347,6 @@ func (q *MetricsViewTimeSeries) buildDuckDBSQL(args []any, mv *runtimev1.Metrics
return sql
}

func TruncateTime(start time.Time, tg runtimev1.TimeGrain, tz *time.Location, firstDay, firstMonth int) time.Time {
switch tg {
case runtimev1.TimeGrain_TIME_GRAIN_MILLISECOND:
return start.Truncate(time.Millisecond)
case runtimev1.TimeGrain_TIME_GRAIN_SECOND:
return start.Truncate(time.Second)
case runtimev1.TimeGrain_TIME_GRAIN_MINUTE:
return start.Truncate(time.Minute)
case runtimev1.TimeGrain_TIME_GRAIN_HOUR:
start = start.In(tz)
start = time.Date(start.Year(), start.Month(), start.Day(), start.Hour(), 0, 0, 0, tz)
return start.In(time.UTC)
case runtimev1.TimeGrain_TIME_GRAIN_DAY:
start = start.In(tz)
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, tz)
return start.In(time.UTC)
case runtimev1.TimeGrain_TIME_GRAIN_WEEK:
start = start.In(tz)
weekday := int(start.Weekday())
if weekday == 0 {
weekday = 7
}
if firstDay < 1 {
firstDay = 1
}
if firstDay > 7 {
firstDay = 7
}

daysToSubtract := -(weekday - firstDay)
if weekday < firstDay {
daysToSubtract = -7 + daysToSubtract
}
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, tz)
start = start.AddDate(0, 0, daysToSubtract)
return start.In(time.UTC)
case runtimev1.TimeGrain_TIME_GRAIN_MONTH:
start = start.In(tz)
start = time.Date(start.Year(), start.Month(), 1, 0, 0, 0, 0, tz)
start = start.In(time.UTC)
return start
case runtimev1.TimeGrain_TIME_GRAIN_QUARTER:
monthsToSubtract := 1 - int(start.Month())%3 // todo first month of year
start = start.In(tz)
start = time.Date(start.Year(), start.Month(), 1, 0, 0, 0, 0, tz)
start = start.AddDate(0, monthsToSubtract, 0)
return start.In(time.UTC)
case runtimev1.TimeGrain_TIME_GRAIN_YEAR:
start = start.In(tz)
year := start.Year()
if int(start.Month()) < firstMonth {
year = start.Year() - 1
}

start = time.Date(year, time.Month(firstMonth), 1, 0, 0, 0, 0, tz)
return start.In(time.UTC)
}

return start
}

func generateNullRecords(schema *runtimev1.StructType) *structpb.Struct {
nullStruct := structpb.Struct{Fields: make(map[string]*structpb.Value, len(schema.Fields))}
for _, f := range schema.Fields {
Expand Down
70 changes: 3 additions & 67 deletions runtime/queries/metricsview_timeseries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
// "fmt"
"testing"
"time"

runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
"github.com/rilldata/rill/runtime"
Expand Down Expand Up @@ -138,7 +137,10 @@ func TestMetricsViewsTimeseries_quarter_grain_IST(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, q.Result)
rows := q.Result.Data
require.Len(t, rows, 6)
i := 0
require.Equal(t, parseTime(t, "2022-10-31T18:30:00Z").AsTime(), rows[i].Ts.AsTime())
i++
require.Equal(t, parseTime(t, "2022-12-31T18:30:00Z").AsTime(), rows[i].Ts.AsTime())
i++
require.Equal(t, parseTime(t, "2023-03-31T18:30:00Z").AsTime(), rows[i].Ts.AsTime())
Expand Down Expand Up @@ -179,69 +181,3 @@ func TestMetricsViewsTimeseries_year_grain_IST(t *testing.T) {
i++
require.Equal(t, parseTime(t, "2023-12-31T18:30:00Z").AsTime(), rows[i].Ts.AsTime())
}

func TestTruncateTime(t *testing.T) {
require.Equal(t, parseTestTime(t, "2019-01-07T04:20:07Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:07.29Z"), runtimev1.TimeGrain_TIME_GRAIN_SECOND, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-07T04:20:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:07Z"), runtimev1.TimeGrain_TIME_GRAIN_MINUTE, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-07T04:00:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_HOUR, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-07T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_DAY, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2023-10-09T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_MONTH, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-04-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2019-05-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_QUARTER, time.UTC, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2019-02-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, time.UTC, 1, 1))
}

func TestTruncateTime_Kathmandu(t *testing.T) {
tz, err := time.LoadLocation("Asia/Kathmandu")
require.NoError(t, err)
require.Equal(t, parseTestTime(t, "2019-01-07T04:20:07Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:07.29Z"), runtimev1.TimeGrain_TIME_GRAIN_SECOND, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-07T04:20:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:07Z"), runtimev1.TimeGrain_TIME_GRAIN_MINUTE, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-07T04:15:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_HOUR, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-06T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2019-01-07T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_DAY, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2023-10-08T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2019-01-31T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2019-02-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_MONTH, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2019-03-31T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2019-05-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_QUARTER, tz, 1, 1))
require.Equal(t, parseTestTime(t, "2018-12-31T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2019-02-07T01:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 1, 1))
}

func TestTruncateTime_UTC_first_day(t *testing.T) {
tz := time.UTC
require.Equal(t, parseTestTime(t, "2023-10-08T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 7, 1))
require.Equal(t, parseTestTime(t, "2023-10-10T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
require.Equal(t, parseTestTime(t, "2023-10-10T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-11T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
require.Equal(t, parseTestTime(t, "2023-10-10T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T00:01:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
}

func TestTruncateTime_Kathmandu_first_day(t *testing.T) {
tz, err := time.LoadLocation("Asia/Kathmandu")
require.NoError(t, err)
require.Equal(t, parseTestTime(t, "2023-10-07T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 7, 1))
require.Equal(t, parseTestTime(t, "2023-10-09T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-10T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
require.Equal(t, parseTestTime(t, "2023-10-09T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-11T04:20:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
require.Equal(t, parseTestTime(t, "2023-10-09T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-09T18:16:01Z"), runtimev1.TimeGrain_TIME_GRAIN_WEEK, tz, 2, 1))
}

func TestTruncateTime_UTC_first_month(t *testing.T) {
tz := time.UTC
require.Equal(t, parseTestTime(t, "2023-02-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-01T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 2))
require.Equal(t, parseTestTime(t, "2023-03-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-01T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 3))
require.Equal(t, parseTestTime(t, "2023-03-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-03-01T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 3))
require.Equal(t, parseTestTime(t, "2022-12-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-01T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 12))
require.Equal(t, parseTestTime(t, "2023-01-01T00:00:00Z"), queries.TruncateTime(parseTestTime(t, "2023-01-01T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 1))
}

func TestTruncateTime_Kathmandu_first_month(t *testing.T) {
tz, err := time.LoadLocation("Asia/Kathmandu")
require.NoError(t, err)
require.Equal(t, parseTestTime(t, "2023-01-31T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-02T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 2))
require.Equal(t, parseTestTime(t, "2023-02-28T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-02T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 3))
require.Equal(t, parseTestTime(t, "2023-02-28T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-03-02T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 3))
require.Equal(t, parseTestTime(t, "2022-11-30T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-10-02T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 12))
require.Equal(t, parseTestTime(t, "2022-12-31T18:15:00Z"), queries.TruncateTime(parseTestTime(t, "2023-01-02T00:20:00Z"), runtimev1.TimeGrain_TIME_GRAIN_YEAR, tz, 2, 1))
}

func parseTestTime(tst *testing.T, t string) time.Time {
ts, err := time.Parse(time.RFC3339, t)
require.NoError(tst, err)
return ts
}
Loading

0 comments on commit 7c36e26

Please sign in to comment.