From e5a1a2a9742f58bcc9cc0bde0bad206f2fe7b9db Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Mon, 15 Mar 2021 22:15:52 +0100 Subject: [PATCH] s3 select: fix date_diff behavior (#11786) Fixes #11785 and adds tests for samples given. --- pkg/s3select/select_test.go | 41 ++++++++++++++++++++++++++++++ pkg/s3select/sql/timestampfuncs.go | 34 +++++++------------------ 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/pkg/s3select/select_test.go b/pkg/s3select/select_test.go index 0f1a01b3f..cce521667 100644 --- a/pkg/s3select/select_test.go +++ b/pkg/s3select/select_test.go @@ -337,6 +337,47 @@ func TestJSONQueries(t *testing.T) { }`, wantResult: `{"element_type":"__elem__merfu","element_id":"d868aefe-ef9a-4be2-b9b2-c9fd89cc43eb","attributes":{"__attr__image_dpi":300,"__attr__image_size":[2550,3299],"__attr__image_index":2,"__attr__image_format":"JPEG","__attr__file_extension":"jpg","__attr__data":null}}`, }, + { + name: "date_diff_month", + query: `SELECT date_diff(MONTH, '2019-10-20T', '2020-01-20T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":3}`, + }, + { + name: "date_diff_month_neg", + query: `SELECT date_diff(MONTH, '2020-01-20T', '2019-10-20T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":-3}`, + }, + // Examples from https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-glacier-select-sql-reference-date.html#s3-glacier-select-sql-reference-date-diff + { + name: "date_diff_year", + query: `SELECT date_diff(year, '2010-01-01T', '2011-01-01T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":1}`, + }, + { + name: "date_diff_year", + query: `SELECT date_diff(month, '2010-01-01T', '2010-05T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":4}`, + }, + { + name: "date_diff_month_oney", + query: `SELECT date_diff(month, '2010T', '2011T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":12}`, + }, + { + name: "date_diff_month_neg", + query: `SELECT date_diff(month, '2011T', '2010T') FROM S3Object LIMIT 1`, + wantResult: `{"_1":-12}`, + }, + { + name: "date_diff_days", + query: `SELECT date_diff(day, '2010-01-01T23:00:00Z', '2010-01-02T01:00:00Z') FROM S3Object LIMIT 1`, + wantResult: `{"_1":0}`, + }, + { + name: "date_diff_days_one", + query: `SELECT date_diff(day, '2010-01-01T23:00:00Z', '2010-01-02T23:00:00Z') FROM S3Object LIMIT 1`, + wantResult: `{"_1":1}`, + }, } defRequest := ` diff --git a/pkg/s3select/sql/timestampfuncs.go b/pkg/s3select/sql/timestampfuncs.go index d8c7f6351..df037a1ca 100644 --- a/pkg/s3select/sql/timestampfuncs.go +++ b/pkg/s3select/sql/timestampfuncs.go @@ -135,10 +135,6 @@ func dateAdd(timePart string, qty float64, t time.Time) (*Value, error) { return FromTimestamp(t.Add(duration)), nil } -const ( - dayInNanoseconds = time.Hour * 24 -) - // dateDiff computes the difference between two times in terms of the // `timePart` which can be years, months, days, hours, minutes or // seconds. For difference in years, months or days, the time part, @@ -146,42 +142,30 @@ const ( func dateDiff(timePart string, ts1, ts2 time.Time) (*Value, error) { if ts2.Before(ts1) { v, err := dateDiff(timePart, ts2, ts1) - v.negate() + if err == nil { + v.negate() + } return v, err } duration := ts2.Sub(ts1) y1, m1, d1 := ts1.Date() y2, m2, d2 := ts2.Date() - dy, dm := int64(y2-y1), int64(m2-m1) switch timePart { case timePartYear: + dy := int64(y2 - y1) if m2 > m1 || (m2 == m1 && d2 >= d1) { return FromInt(dy), nil } return FromInt(dy - 1), nil case timePartMonth: - months := 12 * dy - if m2 >= m1 { - months += dm - } else { - months += 12 + dm - } - if d2 < d1 { - months-- - } - return FromInt(months), nil + m1 += time.Month(12 * y1) + m2 += time.Month(12 * y2) + + return FromInt(int64(m2 - m1)), nil case timePartDay: - // To compute the number of days between two times - // using the time package, zero out the time portions - // of the timestamps, compute the difference duration - // and then divide by the length of a day. - d1 := time.Date(y1, m1, d1, 0, 0, 0, 0, time.UTC) - d2 := time.Date(y2, m2, d2, 0, 0, 0, 0, time.UTC) - diff := d2.Sub(d1) - days := diff / dayInNanoseconds - return FromInt(int64(days)), nil + return FromInt(int64(duration / (24 * time.Hour))), nil case timePartHour: hours := duration / time.Hour return FromInt(int64(hours)), nil