github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/builtins/builtins_test.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package builtins 12 13 import ( 14 "bytes" 15 "fmt" 16 "math/rand" 17 "testing" 18 "time" 19 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 24 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestCategory(t *testing.T) { 30 defer leaktest.AfterTest(t)() 31 if expected, actual := categoryString, builtins["lower"].props.Category; expected != actual { 32 t.Fatalf("bad category: expected %q got %q", expected, actual) 33 } 34 if expected, actual := categoryString, builtins["length"].props.Category; expected != actual { 35 t.Fatalf("bad category: expected %q got %q", expected, actual) 36 } 37 if expected, actual := categoryDateAndTime, builtins["now"].props.Category; expected != actual { 38 t.Fatalf("bad category: expected %q got %q", expected, actual) 39 } 40 if expected, actual := categorySystemInfo, builtins["version"].props.Category; expected != actual { 41 t.Fatalf("bad category: expected %q got %q", expected, actual) 42 } 43 } 44 45 // TestGenerateUniqueIDOrder verifies the expected ordering of 46 // GenerateUniqueID. 47 func TestGenerateUniqueIDOrder(t *testing.T) { 48 defer leaktest.AfterTest(t)() 49 tests := []tree.DInt{ 50 GenerateUniqueID(0, 0), 51 GenerateUniqueID(1, 0), 52 GenerateUniqueID(2<<15, 0), 53 GenerateUniqueID(0, 1), 54 GenerateUniqueID(0, 10000), 55 GenerateUniqueInt(0), 56 } 57 prev := tests[0] 58 for _, tc := range tests[1:] { 59 if tc <= prev { 60 t.Fatalf("%d > %d", tc, prev) 61 } 62 } 63 } 64 65 func TestStringToArrayAndBack(t *testing.T) { 66 defer leaktest.AfterTest(t)() 67 // s allows us to have a string pointer literal. 68 s := func(x string) *string { return &x } 69 fs := func(x *string) string { 70 if x != nil { 71 return *x 72 } 73 return "<nil>" 74 } 75 cases := []struct { 76 input string 77 sep *string 78 nullStr *string 79 expected []*string 80 }{ 81 {`abcxdef`, s(`x`), nil, []*string{s(`abc`), s(`def`)}}, 82 {`xxx`, s(`x`), nil, []*string{s(``), s(``), s(``), s(``)}}, 83 {`xxx`, s(`xx`), nil, []*string{s(``), s(`x`)}}, 84 {`abcxdef`, s(``), nil, []*string{s(`abcxdef`)}}, 85 {`abcxdef`, s(`abcxdef`), nil, []*string{s(``), s(``)}}, 86 {`abcxdef`, s(`x`), s(`abc`), []*string{nil, s(`def`)}}, 87 {`abcxdef`, s(`x`), s(`x`), []*string{s(`abc`), s(`def`)}}, 88 {`abcxdef`, s(`x`), s(``), []*string{s(`abc`), s(`def`)}}, 89 {``, s(`x`), s(``), []*string{}}, 90 {``, s(``), s(``), []*string{}}, 91 {``, s(`x`), nil, []*string{}}, 92 {``, s(``), nil, []*string{}}, 93 {`abcxdef`, nil, nil, []*string{s(`a`), s(`b`), s(`c`), s(`x`), s(`d`), s(`e`), s(`f`)}}, 94 {`abcxdef`, nil, s(`abc`), []*string{s(`a`), s(`b`), s(`c`), s(`x`), s(`d`), s(`e`), s(`f`)}}, 95 {`abcxdef`, nil, s(`x`), []*string{s(`a`), s(`b`), s(`c`), nil, s(`d`), s(`e`), s(`f`)}}, 96 {`abcxdef`, nil, s(``), []*string{s(`a`), s(`b`), s(`c`), s(`x`), s(`d`), s(`e`), s(`f`)}}, 97 {``, nil, s(``), []*string{}}, 98 {``, nil, nil, []*string{}}, 99 } 100 101 for _, tc := range cases { 102 t.Run(fmt.Sprintf("string_to_array(%q, %q)", tc.input, fs(tc.sep)), func(t *testing.T) { 103 result, err := stringToArray(tc.input, tc.sep, tc.nullStr) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 expectedArray := tree.NewDArray(types.String) 109 for _, s := range tc.expected { 110 datum := tree.DNull 111 if s != nil { 112 datum = tree.NewDString(*s) 113 } 114 if err := expectedArray.Append(datum); err != nil { 115 t.Fatal(err) 116 } 117 } 118 119 evalContext := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 120 if result.Compare(evalContext, expectedArray) != 0 { 121 t.Errorf("expected %v, got %v", tc.expected, result) 122 } 123 124 if tc.sep == nil { 125 return 126 } 127 128 s, err := arrayToString(result.(*tree.DArray), *tc.sep, tc.nullStr) 129 if err != nil { 130 t.Fatal(err) 131 } 132 if s == tree.DNull { 133 t.Errorf("expected not null, found null") 134 } 135 136 ds := s.(*tree.DString) 137 fmt.Println(ds) 138 if string(*ds) != tc.input { 139 t.Errorf("original %s, roundtripped %s", tc.input, s) 140 } 141 }) 142 } 143 } 144 145 func TestEscapeFormat(t *testing.T) { 146 defer leaktest.AfterTest(t)() 147 testCases := []struct { 148 bytes []byte 149 str string 150 }{ 151 {[]byte{}, ``}, 152 {[]byte{'a', 'b', 'c'}, `abc`}, 153 {[]byte{'a', 'b', 'c', 'd'}, `abcd`}, 154 {[]byte{'a', 'b', 0, 'd'}, `ab\000d`}, 155 {[]byte{'a', 'b', 0, 0, 'd'}, `ab\000\000d`}, 156 {[]byte{'a', 'b', 0, 'a', 'b', 'c', 0, 'd'}, `ab\000abc\000d`}, 157 {[]byte{'a', 'b', 0, 0}, `ab\000\000`}, 158 {[]byte{'a', 'b', '\\', 'd'}, `ab\\d`}, 159 {[]byte{'a', 'b', 200, 'd'}, `ab\310d`}, 160 {[]byte{'a', 'b', 7, 'd'}, "ab\x07d"}, 161 } 162 163 for _, tc := range testCases { 164 t.Run(tc.str, func(t *testing.T) { 165 result := encodeEscape(tc.bytes) 166 if result != tc.str { 167 t.Fatalf("expected %q, got %q", tc.str, result) 168 } 169 170 decodedResult, err := decodeEscape(tc.str) 171 if err != nil { 172 t.Fatal(err) 173 } 174 if !bytes.Equal(decodedResult, tc.bytes) { 175 t.Fatalf("expected %q, got %#v", tc.bytes, decodedResult) 176 } 177 }) 178 } 179 } 180 181 func TestEscapeFormatRandom(t *testing.T) { 182 defer leaktest.AfterTest(t)() 183 for i := 0; i < 1000; i++ { 184 b := make([]byte, rand.Intn(100)) 185 for j := 0; j < len(b); j++ { 186 b[j] = byte(rand.Intn(256)) 187 } 188 str := encodeEscape(b) 189 decodedResult, err := decodeEscape(str) 190 if err != nil { 191 t.Fatal(err) 192 } 193 if !bytes.Equal(decodedResult, b) { 194 t.Fatalf("generated %#v, after round-tripping got %#v", b, decodedResult) 195 } 196 } 197 } 198 199 func TestLPadRPad(t *testing.T) { 200 defer leaktest.AfterTest(t)() 201 testCases := []struct { 202 padFn func(string, int, string) (string, error) 203 str string 204 length int 205 fill string 206 expected string 207 }{ 208 {lpad, "abc", 1, "xy", "a"}, 209 {lpad, "abc", 2, "xy", "ab"}, 210 {lpad, "abc", 3, "xy", "abc"}, 211 {lpad, "abc", 5, "xy", "xyabc"}, 212 {lpad, "abc", 6, "xy", "xyxabc"}, 213 {lpad, "abc", 7, "xy", "xyxyabc"}, 214 {lpad, "abc", 1, " ", "a"}, 215 {lpad, "abc", 2, " ", "ab"}, 216 {lpad, "abc", 3, " ", "abc"}, 217 {lpad, "abc", 5, " ", " abc"}, 218 {lpad, "Hello, 世界", 9, " ", "Hello, 世界"}, 219 {lpad, "Hello, 世界", 10, " ", " Hello, 世界"}, 220 {lpad, "Hello", 8, "世界", "世界世Hello"}, 221 {lpad, "foo", -1, "世界", ""}, 222 {rpad, "abc", 1, "xy", "a"}, 223 {rpad, "abc", 2, "xy", "ab"}, 224 {rpad, "abc", 3, "xy", "abc"}, 225 {rpad, "abc", 5, "xy", "abcxy"}, 226 {rpad, "abc", 6, "xy", "abcxyx"}, 227 {rpad, "abc", 7, "xy", "abcxyxy"}, 228 {rpad, "abc", 1, " ", "a"}, 229 {rpad, "abc", 2, " ", "ab"}, 230 {rpad, "abc", 3, " ", "abc"}, 231 {rpad, "abc", 5, " ", "abc "}, 232 {rpad, "abc", 5, " ", "abc "}, 233 {rpad, "Hello, 世界", 9, " ", "Hello, 世界"}, 234 {rpad, "Hello, 世界", 10, " ", "Hello, 世界 "}, 235 {rpad, "Hello", 8, "世界", "Hello世界世"}, 236 {rpad, "foo", -1, "世界", ""}, 237 } 238 for _, tc := range testCases { 239 out, err := tc.padFn(tc.str, tc.length, tc.fill) 240 if err != nil { 241 t.Errorf("Found err %v, expected nil", err) 242 } 243 if out != tc.expected { 244 t.Errorf("expected %s, found %s", tc.expected, out) 245 } 246 } 247 } 248 249 func TestExtractTimeSpanFromTimestamp(t *testing.T) { 250 defer leaktest.AfterTest(t)() 251 252 utcPositiveOffset := time.FixedZone("otan happy time", 60*60*4+30*60) 253 utcNegativeOffset := time.FixedZone("otan sad time", -60*60*4-30*60) 254 255 testCases := []struct { 256 input time.Time 257 timeSpan string 258 259 expected tree.DFloat 260 expectedError string 261 }{ 262 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "timezone", expected: 0}, 263 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "timezone_hour", expected: 0}, 264 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "timezone_minute", expected: 0}, 265 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "millennia", expected: 3}, 266 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "century", expected: 21}, 267 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "decade", expected: 201}, 268 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "year", expected: 2019}, 269 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "month", expected: 12}, 270 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "day", expected: 11}, 271 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "hour", expected: 0}, 272 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "minute", expected: 14}, 273 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "second", expected: 15.123456}, 274 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "millisecond", expected: 15123.456}, 275 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, time.UTC), timeSpan: "microsecond", expected: 15123456}, 276 277 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "timezone", expected: 4*60*60 + 30*60}, 278 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "timezone_hour", expected: 4}, 279 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "timezone_minute", expected: 30}, 280 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "millennia", expected: 3}, 281 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "century", expected: 21}, 282 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "decade", expected: 201}, 283 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "year", expected: 2019}, 284 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "month", expected: 12}, 285 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "day", expected: 11}, 286 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "hour", expected: 0}, 287 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "minute", expected: 14}, 288 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "second", expected: 15.123456}, 289 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "millisecond", expected: 15123.456}, 290 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcPositiveOffset), timeSpan: "microsecond", expected: 15123456}, 291 292 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "timezone", expected: -4*60*60 - 30*60}, 293 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "timezone_hour", expected: -4}, 294 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "timezone_minute", expected: -30}, 295 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "millennia", expected: 3}, 296 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "century", expected: 21}, 297 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "decade", expected: 201}, 298 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "year", expected: 2019}, 299 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "month", expected: 12}, 300 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "day", expected: 11}, 301 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "hour", expected: 0}, 302 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "minute", expected: 14}, 303 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "second", expected: 15.123456}, 304 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "millisecond", expected: 15123.456}, 305 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "microsecond", expected: 15123456}, 306 307 {input: time.Date(2019, time.December, 11, 0, 14, 15, 123456000, utcNegativeOffset), timeSpan: "it's numberwang!", expectedError: "unsupported timespan: it's numberwang!"}, 308 } 309 310 for _, tc := range testCases { 311 t.Run(fmt.Sprintf("%s_%s", tc.timeSpan, tc.input.Format(time.RFC3339)), func(t *testing.T) { 312 datum, err := extractTimeSpanFromTimestampTZ(nil, tc.input, tc.timeSpan) 313 if tc.expectedError != "" { 314 assert.EqualError(t, err, tc.expectedError) 315 } else { 316 assert.NoError(t, err) 317 assert.Equal(t, tc.expected, *(datum.(*tree.DFloat))) 318 } 319 }) 320 } 321 } 322 323 func TestExtractTimeSpanFromTimeTZ(t *testing.T) { 324 defer leaktest.AfterTest(t)() 325 326 testCases := []struct { 327 timeTZString string 328 timeSpan string 329 expected tree.DFloat 330 expectedError string 331 }{ 332 {timeTZString: "11:12:13+01:02", timeSpan: "hour", expected: 11}, 333 {timeTZString: "11:12:13+01:02", timeSpan: "minute", expected: 12}, 334 {timeTZString: "11:12:13+01:02", timeSpan: "second", expected: 13}, 335 {timeTZString: "11:12:13.123456+01:02", timeSpan: "millisecond", expected: 13123.456}, 336 {timeTZString: "11:12:13.123456+01:02", timeSpan: "microsecond", expected: 13123456}, 337 {timeTZString: "11:12:13+01:02", timeSpan: "timezone", expected: 3720}, 338 {timeTZString: "11:12:13+01:02", timeSpan: "timezone_hour", expected: 1}, 339 {timeTZString: "11:12:13+01:02", timeSpan: "timezone_minute", expected: 2}, 340 {timeTZString: "11:12:13-01:02", timeSpan: "timezone", expected: -3720}, 341 {timeTZString: "11:12:13-01:02", timeSpan: "timezone_hour", expected: -1}, 342 {timeTZString: "11:12:13-01:02", timeSpan: "timezone_minute", expected: -2}, 343 {timeTZString: "11:12:13.5+01:02", timeSpan: "epoch", expected: 36613.5}, 344 {timeTZString: "11:12:13.5-01:02", timeSpan: "epoch", expected: 44053.5}, 345 346 {timeTZString: "11:12:13-01:02", timeSpan: "epoch2", expectedError: "unsupported timespan: epoch2"}, 347 } 348 349 for _, tc := range testCases { 350 t.Run(fmt.Sprintf("%s_%s", tc.timeSpan, tc.timeTZString), func(t *testing.T) { 351 timeTZ, err := tree.ParseDTimeTZ(nil, tc.timeTZString, time.Microsecond) 352 assert.NoError(t, err) 353 354 datum, err := extractTimeSpanFromTimeTZ(timeTZ, tc.timeSpan) 355 if tc.expectedError != "" { 356 assert.EqualError(t, err, tc.expectedError) 357 } else { 358 assert.NoError(t, err) 359 assert.Equal(t, tc.expected, *(datum.(*tree.DFloat))) 360 } 361 }) 362 } 363 } 364 365 func TestExtractTimeSpanFromInterval(t *testing.T) { 366 defer leaktest.AfterTest(t)() 367 368 testCases := []struct { 369 timeSpan string 370 intervalStr string 371 expected *tree.DFloat 372 }{ 373 {"millennia", "25000 months 1000 days", tree.NewDFloat(2)}, 374 {"millennia", "-25000 months 1000 days", tree.NewDFloat(-2)}, 375 {"millennium", "25000 months 1000 days", tree.NewDFloat(2)}, 376 {"millenniums", "25000 months 1000 days", tree.NewDFloat(2)}, 377 378 {"century", "25000 months 1000 days", tree.NewDFloat(20)}, 379 {"century", "-25000 months 1000 days", tree.NewDFloat(-20)}, 380 {"centuries", "25000 months 1000 days", tree.NewDFloat(20)}, 381 382 {"decade", "25000 months 1000 days", tree.NewDFloat(208)}, 383 {"decade", "-25000 months 1000 days", tree.NewDFloat(-208)}, 384 {"decades", "25000 months 1000 days", tree.NewDFloat(208)}, 385 386 {"year", "25000 months 1000 days", tree.NewDFloat(2083)}, 387 {"year", "-25000 months 1000 days", tree.NewDFloat(-2083)}, 388 {"years", "25000 months 1000 days", tree.NewDFloat(2083)}, 389 390 {"month", "25000 months 1000 days", tree.NewDFloat(4)}, 391 {"month", "-25000 months 1000 days", tree.NewDFloat(-4)}, 392 {"months", "25000 months 1000 days", tree.NewDFloat(4)}, 393 394 {"day", "25000 months 1000 days", tree.NewDFloat(1000)}, 395 {"day", "-25000 months 1000 days", tree.NewDFloat(1000)}, 396 {"day", "-25000 months -1000 days", tree.NewDFloat(-1000)}, 397 {"days", "25000 months 1000 days", tree.NewDFloat(1000)}, 398 399 {"hour", "25-1 100:56:01.123456", tree.NewDFloat(100)}, 400 {"hour", "25-1 -100:56:01.123456", tree.NewDFloat(-100)}, 401 {"hours", "25-1 100:56:01.123456", tree.NewDFloat(100)}, 402 403 {"minute", "25-1 100:56:01.123456", tree.NewDFloat(56)}, 404 {"minute", "25-1 -100:56:01.123456", tree.NewDFloat(-56)}, 405 {"minutes", "25-1 100:56:01.123456", tree.NewDFloat(56)}, 406 407 {"second", "25-1 100:56:01.123456", tree.NewDFloat(1.123456)}, 408 {"second", "25-1 -100:56:01.123456", tree.NewDFloat(-1.123456)}, 409 {"seconds", "25-1 100:56:01.123456", tree.NewDFloat(1.123456)}, 410 411 {"millisecond", "25-1 100:56:01.123456", tree.NewDFloat(1123.456)}, 412 {"millisecond", "25-1 -100:56:01.123456", tree.NewDFloat(-1123.456)}, 413 {"milliseconds", "25-1 100:56:01.123456", tree.NewDFloat(1123.456)}, 414 415 {"microsecond", "25-1 100:56:01.123456", tree.NewDFloat(1123456)}, 416 {"microsecond", "25-1 -100:56:01.123456", tree.NewDFloat(-1123456)}, 417 {"microseconds", "25-1 100:56:01.123456", tree.NewDFloat(1123456)}, 418 419 {"epoch", "25-1 100:56:01.123456", tree.NewDFloat(791895361.123456)}, 420 {"epoch", "25-1 -100:56:01.123456", tree.NewDFloat(791168638.876544)}, 421 } 422 423 for _, tc := range testCases { 424 t.Run(fmt.Sprintf("%s as %s", tc.intervalStr, tc.timeSpan), func(t *testing.T) { 425 interval, err := tree.ParseDInterval(tc.intervalStr) 426 assert.NoError(t, err) 427 428 d, err := extractTimeSpanFromInterval(interval, tc.timeSpan) 429 assert.NoError(t, err) 430 431 assert.Equal(t, *tc.expected, *(d.(*tree.DFloat))) 432 }) 433 } 434 } 435 436 func TestTruncateTimestamp(t *testing.T) { 437 defer leaktest.AfterTest(t)() 438 439 loc, err := timeutil.LoadLocation("Australia/Sydney") 440 require.NoError(t, err) 441 442 testCases := []struct { 443 fromTime time.Time 444 timeSpan string 445 expected *tree.DTimestampTZ 446 }{ 447 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "millennium", tree.MustMakeDTimestampTZ(time.Date(2001, time.January, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 448 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "century", tree.MustMakeDTimestampTZ(time.Date(2101, time.January, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 449 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "decade", tree.MustMakeDTimestampTZ(time.Date(2110, time.January, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 450 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "year", tree.MustMakeDTimestampTZ(time.Date(2118, time.January, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 451 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "quarter", tree.MustMakeDTimestampTZ(time.Date(2118, time.January, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 452 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "month", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 1, 0, 0, 0, 0, loc), time.Microsecond)}, 453 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "day", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 11, 0, 0, 0, 0, loc), time.Microsecond)}, 454 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "week", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 7, 0, 0, 0, 0, loc), time.Microsecond)}, 455 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "hour", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 11, 5, 0, 0, 0, loc), time.Microsecond)}, 456 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "second", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 11, 5, 6, 7, 0, loc), time.Microsecond)}, 457 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "millisecond", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 11, 5, 6, 7, 80000000, loc), time.Microsecond)}, 458 {time.Date(2118, time.March, 11, 5, 6, 7, 80009001, loc), "microsecond", tree.MustMakeDTimestampTZ(time.Date(2118, time.March, 11, 5, 6, 7, 80009000, loc), time.Microsecond)}, 459 460 // Test Monday and Sunday boundaries. 461 {time.Date(2019, time.November, 11, 5, 6, 7, 80009001, loc), "week", tree.MustMakeDTimestampTZ(time.Date(2019, time.November, 11, 0, 0, 0, 0, loc), time.Microsecond)}, 462 {time.Date(2019, time.November, 10, 5, 6, 7, 80009001, loc), "week", tree.MustMakeDTimestampTZ(time.Date(2019, time.November, 4, 0, 0, 0, 0, loc), time.Microsecond)}, 463 } 464 465 for _, tc := range testCases { 466 t.Run(tc.timeSpan, func(t *testing.T) { 467 result, err := truncateTimestamp(nil, tc.fromTime, tc.timeSpan) 468 require.NoError(t, err) 469 assert.Equal(t, tc.expected, result) 470 }) 471 } 472 }