@malloydata/malloy-tests 0.0.312 → 0.0.313
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@jest/globals": "^29.4.3",
|
|
24
|
-
"@malloydata/db-bigquery": "0.0.
|
|
25
|
-
"@malloydata/db-duckdb": "0.0.
|
|
26
|
-
"@malloydata/db-postgres": "0.0.
|
|
27
|
-
"@malloydata/db-snowflake": "0.0.
|
|
28
|
-
"@malloydata/db-trino": "0.0.
|
|
29
|
-
"@malloydata/malloy": "0.0.
|
|
30
|
-
"@malloydata/malloy-tag": "0.0.
|
|
31
|
-
"@malloydata/render": "0.0.
|
|
24
|
+
"@malloydata/db-bigquery": "0.0.313",
|
|
25
|
+
"@malloydata/db-duckdb": "0.0.313",
|
|
26
|
+
"@malloydata/db-postgres": "0.0.313",
|
|
27
|
+
"@malloydata/db-snowflake": "0.0.313",
|
|
28
|
+
"@malloydata/db-trino": "0.0.313",
|
|
29
|
+
"@malloydata/malloy": "0.0.313",
|
|
30
|
+
"@malloydata/malloy-tag": "0.0.313",
|
|
31
|
+
"@malloydata/render": "0.0.313",
|
|
32
32
|
"events": "^3.3.0",
|
|
33
33
|
"jsdom": "^22.1.0",
|
|
34
34
|
"luxon": "^2.4.0",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@types/jsdom": "^21.1.1",
|
|
39
39
|
"@types/luxon": "^2.4.0"
|
|
40
40
|
},
|
|
41
|
-
"version": "0.0.
|
|
41
|
+
"version": "0.0.313"
|
|
42
42
|
}
|
|
@@ -421,55 +421,78 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
421
421
|
});
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
-
type TL = 'timeLiteral';
|
|
425
|
-
|
|
426
424
|
describe('temporal filters', () => {
|
|
425
|
+
function tsLit(at: LuxonDateTime): string {
|
|
426
|
+
const typeDef: {type: 'timestamp' | 'date'} = {type: 'timestamp'};
|
|
427
|
+
const node: {node: 'timeLiteral'} = {node: 'timeLiteral'};
|
|
428
|
+
const timeStr = at.toUTC().toFormat(fTimestamp);
|
|
429
|
+
const n = {...node, typeDef, literal: timeStr};
|
|
430
|
+
return db.dialect.sqlLiteralTime({}, n);
|
|
431
|
+
}
|
|
427
432
|
function lit(t: string, type: 'timestamp' | 'date'): string {
|
|
428
433
|
const typeDef: {type: 'timestamp' | 'date'} = {type};
|
|
429
|
-
const
|
|
430
|
-
const n = {
|
|
431
|
-
node: timeLiteral,
|
|
432
|
-
typeDef,
|
|
433
|
-
literal: t,
|
|
434
|
-
};
|
|
434
|
+
const node: {node: 'timeLiteral'} = {node: 'timeLiteral'};
|
|
435
|
+
const n = {...node, typeDef, literal: t};
|
|
435
436
|
return db.dialect.sqlLiteralTime({}, n);
|
|
436
437
|
}
|
|
437
438
|
|
|
438
439
|
const fTimestamp = 'yyyy-LL-dd HH:mm:ss';
|
|
439
440
|
const fDate = 'yyyy-LL-dd';
|
|
440
441
|
|
|
442
|
+
const inRange = [{n: 'first'}, {n: 'last'}];
|
|
443
|
+
const notInRange = [{n: 'before'}, {n: 'post-range'}];
|
|
444
|
+
|
|
441
445
|
/**
|
|
442
|
-
* Create a
|
|
446
|
+
* Create a query for testing temporal filters with better timezone handling.
|
|
447
|
+
* Returns a complete Malloy query string with the filter and timezone in the same segment.
|
|
448
|
+
* Result will have five rows:
|
|
443
449
|
* { t: 1 second before start, n: 'before' }
|
|
444
450
|
* { t: start, n: 'first' }
|
|
445
451
|
* { t: 1 second before end, n: 'last' }
|
|
446
452
|
* { t: end, n: 'post-range' }
|
|
447
453
|
* { t: NULL n: 'z-null' }
|
|
448
454
|
* Use malloyResultMatches(range, inRange) or (range, notInRange)
|
|
455
|
+
*
|
|
456
|
+
* If a timezone is provided then ...
|
|
457
|
+
* - the start and end times are considered to be in that timezone
|
|
458
|
+
* - the query generated will include a timezone: directive
|
|
459
|
+
* - the filter expression is evaluated in that timezone
|
|
449
460
|
*/
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
461
|
+
function mkRangeQuery(
|
|
462
|
+
filterExpr: string,
|
|
463
|
+
start: string,
|
|
464
|
+
end: string,
|
|
465
|
+
queryTimezone?: string
|
|
466
|
+
): string {
|
|
467
|
+
const zone = queryTimezone ?? 'UTC';
|
|
468
|
+
|
|
469
|
+
// Convert the civil time to the desired timezone
|
|
470
|
+
const begin = LuxonDateTime.fromFormat(start, fTimestamp, {zone});
|
|
471
|
+
const endTime = LuxonDateTime.fromFormat(end, fTimestamp, {zone});
|
|
472
|
+
|
|
454
473
|
const b4 = begin.minus({second: 1});
|
|
455
|
-
const last =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
'
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
UNION ALL SELECT ${
|
|
466
|
-
UNION ALL SELECT ${
|
|
467
|
-
UNION ALL SELECT ${lit(end, 'timestamp')}, 'post-range'
|
|
474
|
+
const last = endTime.minus({second: 1});
|
|
475
|
+
|
|
476
|
+
const timezoneClause = queryTimezone
|
|
477
|
+
? `timezone: '${queryTimezone}';`
|
|
478
|
+
: '';
|
|
479
|
+
|
|
480
|
+
return `
|
|
481
|
+
run: ${dbName}.sql("""
|
|
482
|
+
SELECT ${tsLit(b4)} AS ${q`t`}, 'before' AS ${q`n`}
|
|
483
|
+
UNION ALL SELECT ${tsLit(begin)}, 'first'
|
|
484
|
+
UNION ALL SELECT ${tsLit(last)} , 'last'
|
|
485
|
+
UNION ALL SELECT ${tsLit(endTime)}, 'post-range'
|
|
468
486
|
UNION ALL SELECT NULL, 'z-null'
|
|
469
|
-
""")
|
|
470
|
-
|
|
471
|
-
|
|
487
|
+
""") -> {
|
|
488
|
+
${timezoneClause}
|
|
489
|
+
where: t ~ ${filterExpr}
|
|
490
|
+
select: t, n
|
|
491
|
+
order_by: n
|
|
492
|
+
}
|
|
493
|
+
`;
|
|
472
494
|
}
|
|
495
|
+
|
|
473
496
|
function mkDateRange(start: string, end: string) {
|
|
474
497
|
const begin = LuxonDateTime.fromFormat(start, fDate);
|
|
475
498
|
const b4 = begin.minus({day: 1});
|
|
@@ -499,9 +522,12 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
499
522
|
/**
|
|
500
523
|
* All the relative time tests need a way to set what time it is now
|
|
501
524
|
*/
|
|
502
|
-
function nowIs(
|
|
525
|
+
function nowIs(nowStr: string, zone = 'UTC') {
|
|
503
526
|
const spyNow = jest.spyOn(db.dialect, 'sqlNowExpr');
|
|
504
|
-
spyNow.mockImplementation(() =>
|
|
527
|
+
spyNow.mockImplementation(() => {
|
|
528
|
+
const utcTime = LuxonDateTime.fromFormat(nowStr, fTimestamp, {zone});
|
|
529
|
+
return tsLit(utcTime);
|
|
530
|
+
});
|
|
505
531
|
}
|
|
506
532
|
afterEach(() => jest.restoreAllMocks());
|
|
507
533
|
|
|
@@ -525,110 +551,144 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
525
551
|
});
|
|
526
552
|
test('2 days ago', async () => {
|
|
527
553
|
nowIs('2001-01-15 12:00:00');
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
554
|
+
const rangeQuery = mkRangeQuery(
|
|
555
|
+
"f'2 days ago'",
|
|
556
|
+
'2001-01-13 00:00:00',
|
|
557
|
+
'2001-01-14 00:00:00'
|
|
558
|
+
);
|
|
559
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
532
560
|
});
|
|
533
561
|
test('2 days', async () => {
|
|
534
562
|
nowIs('2001-01-15 12:00:00');
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
563
|
+
const rangeQuery = mkRangeQuery(
|
|
564
|
+
"f'2 days'",
|
|
565
|
+
'2001-01-14 00:00:00',
|
|
566
|
+
'2001-01-16 00:00:00'
|
|
567
|
+
);
|
|
568
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
539
569
|
});
|
|
540
570
|
test('2 days from now', async () => {
|
|
541
571
|
nowIs('2001-01-15 12:00:00');
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
572
|
+
const rangeQuery = mkRangeQuery(
|
|
573
|
+
"f'2 days from now'",
|
|
574
|
+
'2001-01-17 00:00:00',
|
|
575
|
+
'2001-01-18 00:00:00'
|
|
576
|
+
);
|
|
577
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
546
578
|
});
|
|
547
579
|
test('2000 to 2001', async () => {
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
580
|
+
const rangeQuery = mkRangeQuery(
|
|
581
|
+
"f'2000 to 2001'",
|
|
582
|
+
'2000-01-01 00:00:00',
|
|
583
|
+
'2001-01-01 00:00:00'
|
|
584
|
+
);
|
|
585
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
552
586
|
});
|
|
553
587
|
test('next 2 days', async () => {
|
|
554
588
|
nowIs('2001-01-01 12:00:00');
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
589
|
+
const rangeQuery = mkRangeQuery(
|
|
590
|
+
"f'next 2 days'",
|
|
591
|
+
'2001-01-02 00:00:00',
|
|
592
|
+
'2001-01-04 00:00:00'
|
|
593
|
+
);
|
|
594
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
559
595
|
});
|
|
560
596
|
test('last 2 months', async () => {
|
|
561
597
|
nowIs('2001-01-01 12:00:00');
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
598
|
+
const rangeQuery = mkRangeQuery(
|
|
599
|
+
"f'last 2 months'",
|
|
600
|
+
'2000-11-01 00:00:00',
|
|
601
|
+
'2001-01-01 00:00:00'
|
|
602
|
+
);
|
|
603
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
566
604
|
});
|
|
567
605
|
test('before y2k', async () => {
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
606
|
+
const rangeQuery = mkRangeQuery(
|
|
607
|
+
"f'before 2001'",
|
|
608
|
+
'2001-01-01 00:00:00',
|
|
609
|
+
'2002-01-01 00:00:00'
|
|
610
|
+
);
|
|
611
|
+
await expect(rangeQuery).malloyResultMatches(db, [{n: 'before'}]);
|
|
572
612
|
});
|
|
573
613
|
test('after y2k', async () => {
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
614
|
+
const rangeQuery = mkRangeQuery(
|
|
615
|
+
"f'after 2001'",
|
|
616
|
+
'2001-01-01 00:00:00',
|
|
617
|
+
'2002-01-01 00:00:00'
|
|
618
|
+
);
|
|
619
|
+
await expect(rangeQuery).malloyResultMatches(db, [{n: 'post-range'}]);
|
|
578
620
|
});
|
|
579
621
|
test('y2k for 1 minute', async () => {
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
622
|
+
const rangeQuery = mkRangeQuery(
|
|
623
|
+
"f'2001 for 1 minute'",
|
|
624
|
+
'2001-01-01 00:00:00',
|
|
625
|
+
'2001-01-01 00:01:00'
|
|
626
|
+
);
|
|
627
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
584
628
|
});
|
|
585
629
|
test('y2k for 2 hour', async () => {
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
630
|
+
const rangeQuery = mkRangeQuery(
|
|
631
|
+
"f'2001 for 2 hour'",
|
|
632
|
+
'2001-01-01 00:00:00',
|
|
633
|
+
'2001-01-01 02:00:00'
|
|
634
|
+
);
|
|
635
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
590
636
|
});
|
|
591
637
|
test('y2k for 1 day', async () => {
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
638
|
+
const rangeQuery = mkRangeQuery(
|
|
639
|
+
"f'2001 for 1 day'",
|
|
640
|
+
'2001-01-01 00:00:00',
|
|
641
|
+
'2001-01-02 00:00:00'
|
|
642
|
+
);
|
|
643
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
596
644
|
});
|
|
597
645
|
test('y2k for 1 week', async () => {
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
646
|
+
const rangeQuery = mkRangeQuery(
|
|
647
|
+
"f'2001 for 1 week'",
|
|
648
|
+
'2001-01-01 00:00:00',
|
|
649
|
+
'2001-01-08 00:00:00'
|
|
650
|
+
);
|
|
651
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
602
652
|
});
|
|
603
653
|
test('y2k for 1 month', async () => {
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
654
|
+
const rangeQuery = mkRangeQuery(
|
|
655
|
+
"f'2001 for 1 month'",
|
|
656
|
+
'2001-01-01 00:00:00',
|
|
657
|
+
'2001-02-01 00:00:00'
|
|
658
|
+
);
|
|
659
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
608
660
|
});
|
|
609
661
|
test('y2k for 1 quarter', async () => {
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
662
|
+
const rangeQuery = mkRangeQuery(
|
|
663
|
+
"f'2001 for 1 quarter'",
|
|
664
|
+
'2001-01-01 00:00:00',
|
|
665
|
+
'2001-04-01 00:00:00'
|
|
666
|
+
);
|
|
667
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
614
668
|
});
|
|
615
669
|
test('y2k for 1 year', async () => {
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
670
|
+
const rangeQuery = mkRangeQuery(
|
|
671
|
+
"f'2001 for 1 year'",
|
|
672
|
+
'2001-01-01 00:00:00',
|
|
673
|
+
'2002-01-01 00:00:00'
|
|
674
|
+
);
|
|
675
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
620
676
|
});
|
|
621
677
|
test('null', async () => {
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
678
|
+
const rangeQuery = mkRangeQuery(
|
|
679
|
+
"f'null'",
|
|
680
|
+
'2001-01-01 00:00:00',
|
|
681
|
+
'2002-01-01 00:00:00'
|
|
682
|
+
);
|
|
683
|
+
await expect(rangeQuery).malloyResultMatches(db, [{n: 'z-null'}]);
|
|
626
684
|
});
|
|
627
685
|
test('not null', async () => {
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
686
|
+
const rangeQuery = mkRangeQuery(
|
|
687
|
+
"f'not null'",
|
|
688
|
+
'2001-01-01 00:00:00',
|
|
689
|
+
'2002-01-01 00:00:00'
|
|
690
|
+
);
|
|
691
|
+
await expect(rangeQuery).malloyResultMatches(db, [
|
|
632
692
|
{n: 'before'},
|
|
633
693
|
{n: 'first'},
|
|
634
694
|
{n: 'last'},
|
|
@@ -636,11 +696,12 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
636
696
|
]);
|
|
637
697
|
});
|
|
638
698
|
test('empty temporal filter', async () => {
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
699
|
+
const rangeQuery = mkRangeQuery(
|
|
700
|
+
"f''",
|
|
701
|
+
'2001-01-01 00:00:00',
|
|
702
|
+
'2002-01-01 00:00:00'
|
|
703
|
+
);
|
|
704
|
+
await expect(rangeQuery).malloyResultMatches(db, [
|
|
644
705
|
{n: 'before'},
|
|
645
706
|
{n: 'first'},
|
|
646
707
|
{n: 'last'},
|
|
@@ -649,46 +710,60 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
649
710
|
]);
|
|
650
711
|
});
|
|
651
712
|
test('year literal', async () => {
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
713
|
+
const rangeQuery = mkRangeQuery(
|
|
714
|
+
"f'2001'",
|
|
715
|
+
'2001-01-01 00:00:00',
|
|
716
|
+
'2002-01-01 00:00:00'
|
|
717
|
+
);
|
|
718
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
656
719
|
});
|
|
657
720
|
test('not month literal', async () => {
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
721
|
+
const rangeQuery = mkRangeQuery(
|
|
722
|
+
"f'not 2001-06'",
|
|
723
|
+
'2001-06-01 00:00:00',
|
|
724
|
+
'2001-07-01 00:00:00'
|
|
725
|
+
);
|
|
726
|
+
await expect(rangeQuery).malloyResultMatches(db, notInRange);
|
|
662
727
|
});
|
|
663
728
|
test('day literal', async () => {
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
729
|
+
const rangeQuery = mkRangeQuery(
|
|
730
|
+
"f'2001-06-15'",
|
|
731
|
+
'2001-06-15 00:00:00',
|
|
732
|
+
'2001-06-16 00:00:00'
|
|
733
|
+
);
|
|
734
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
668
735
|
});
|
|
669
736
|
test('hour literal', async () => {
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
737
|
+
const rangeQuery = mkRangeQuery(
|
|
738
|
+
"f'2001-02-03 04'",
|
|
739
|
+
'2001-02-03 04:00:00',
|
|
740
|
+
'2001-02-03 05:00:00'
|
|
741
|
+
);
|
|
742
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
674
743
|
});
|
|
675
744
|
test('minute literal', async () => {
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
745
|
+
const rangeQuery = mkRangeQuery(
|
|
746
|
+
"f'2001-02-03 04:05'",
|
|
747
|
+
'2001-02-03 04:05:00',
|
|
748
|
+
'2001-02-03 04:06:00'
|
|
749
|
+
);
|
|
750
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
680
751
|
});
|
|
681
752
|
test('quarter literal', async () => {
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
753
|
+
const rangeQuery = mkRangeQuery(
|
|
754
|
+
"f'2001-Q1'",
|
|
755
|
+
'2001-01-01 00:00:00',
|
|
756
|
+
'2001-04-01 00:00:00'
|
|
757
|
+
);
|
|
758
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
686
759
|
});
|
|
687
760
|
test('week literal', async () => {
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
761
|
+
const rangeQuery = mkRangeQuery(
|
|
762
|
+
"f'2023-01-01-WK'",
|
|
763
|
+
'2023-01-01 00:00:00',
|
|
764
|
+
'2023-01-08 00:00:00'
|
|
765
|
+
);
|
|
766
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
692
767
|
});
|
|
693
768
|
test('full second literal', async () => {
|
|
694
769
|
const eqtime = mkEqTime('2023-01-01 01:02:03');
|
|
@@ -704,163 +779,250 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
704
779
|
});
|
|
705
780
|
test('today', async () => {
|
|
706
781
|
nowIs('2001-02-03 12:00:00');
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
782
|
+
const rangeQuery = mkRangeQuery(
|
|
783
|
+
"f'today'",
|
|
784
|
+
'2001-02-03 00:00:00',
|
|
785
|
+
'2001-02-04 00:00:00'
|
|
786
|
+
);
|
|
787
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
711
788
|
});
|
|
712
789
|
test('yesterday', async () => {
|
|
713
790
|
nowIs('2001-02-03 12:00:00');
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
791
|
+
const rangeQuery = mkRangeQuery(
|
|
792
|
+
"f'yesterday'",
|
|
793
|
+
'2001-02-02 00:00:00',
|
|
794
|
+
'2001-02-03 00:00:00'
|
|
795
|
+
);
|
|
796
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
718
797
|
});
|
|
719
798
|
test('tomorrow', async () => {
|
|
720
799
|
nowIs('2001-02-03 12:00:00');
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
800
|
+
const rangeQuery = mkRangeQuery(
|
|
801
|
+
"f'tomorrow'",
|
|
802
|
+
'2001-02-04 00:00:00',
|
|
803
|
+
'2001-02-05 00:00:00'
|
|
804
|
+
);
|
|
805
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
725
806
|
});
|
|
726
807
|
test('this week', async () => {
|
|
727
808
|
nowIs('2023-01-03 00:00:00');
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
809
|
+
const rangeQuery = mkRangeQuery(
|
|
810
|
+
"f'this week'",
|
|
811
|
+
'2023-01-01 00:00:00',
|
|
812
|
+
'2023-01-08 00:00:00'
|
|
813
|
+
);
|
|
814
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
732
815
|
});
|
|
733
816
|
test('last month', async () => {
|
|
734
817
|
nowIs('2001-02-01 12:00:00');
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
818
|
+
const rangeQuery = mkRangeQuery(
|
|
819
|
+
"f'last month'",
|
|
820
|
+
'2001-01-01 00:00:00',
|
|
821
|
+
'2001-02-01 00:00:00'
|
|
822
|
+
);
|
|
823
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
739
824
|
});
|
|
740
825
|
test('next quarter', async () => {
|
|
741
826
|
nowIs('2001-01-02 12:00:00');
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
827
|
+
const rangeQuery = mkRangeQuery(
|
|
828
|
+
"f'next quarter'",
|
|
829
|
+
'2001-04-01 00:00:00',
|
|
830
|
+
'2001-07-01 00:00:00'
|
|
831
|
+
);
|
|
832
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
746
833
|
});
|
|
747
834
|
test('this year', async () => {
|
|
748
835
|
nowIs('2001-01-02 12:00:00');
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
836
|
+
const rangeQuery = mkRangeQuery(
|
|
837
|
+
"f'this year'",
|
|
838
|
+
'2001-01-01 00:00:00',
|
|
839
|
+
'2002-01-01 00:00:00'
|
|
840
|
+
);
|
|
841
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
753
842
|
});
|
|
754
843
|
// 2023-01-01 is a sunday
|
|
755
844
|
test('(last) sunday', async () => {
|
|
756
845
|
nowIs('2023-01-03 00:00:00');
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
846
|
+
const rangeQuery = mkRangeQuery(
|
|
847
|
+
"f'sunday'",
|
|
848
|
+
'2023-01-01 00:00:00',
|
|
849
|
+
'2023-01-02 00:00:00'
|
|
850
|
+
);
|
|
851
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
761
852
|
});
|
|
762
853
|
test('last monday', async () => {
|
|
763
854
|
nowIs('2023-01-03 00:00:00');
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
855
|
+
const rangeQuery = mkRangeQuery(
|
|
856
|
+
"f'last monday'",
|
|
857
|
+
'2023-01-02 00:00:00',
|
|
858
|
+
'2023-01-03 00:00:00'
|
|
859
|
+
);
|
|
860
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
768
861
|
});
|
|
769
862
|
test('last-tuesday', async () => {
|
|
770
863
|
nowIs('2023-01-03 00:00:00');
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
864
|
+
const rangeQuery = mkRangeQuery(
|
|
865
|
+
"f'tuesday'",
|
|
866
|
+
'2022-12-27 00:00:00',
|
|
867
|
+
'2022-12-28 00:00:00'
|
|
868
|
+
);
|
|
869
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
775
870
|
});
|
|
776
871
|
test('last-wednesday', async () => {
|
|
777
872
|
nowIs('2023-01-03 00:00:00');
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
873
|
+
const rangeQuery = mkRangeQuery(
|
|
874
|
+
"f'wednesday'",
|
|
875
|
+
'2022-12-28 00:00:00',
|
|
876
|
+
'2022-12-29 00:00:00'
|
|
877
|
+
);
|
|
878
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
782
879
|
});
|
|
783
880
|
test('last-thursday', async () => {
|
|
784
881
|
nowIs('2023-01-03 00:00:00');
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
882
|
+
const rangeQuery = mkRangeQuery(
|
|
883
|
+
"f'thursday'",
|
|
884
|
+
'2022-12-29 00:00:00',
|
|
885
|
+
'2022-12-30 00:00:00'
|
|
886
|
+
);
|
|
887
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
789
888
|
});
|
|
790
889
|
test('last-friday', async () => {
|
|
791
890
|
nowIs('2023-01-03 00:00:00');
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
891
|
+
const rangeQuery = mkRangeQuery(
|
|
892
|
+
"f'friday'",
|
|
893
|
+
'2022-12-30 00:00:00',
|
|
894
|
+
'2022-12-31 00:00:00'
|
|
895
|
+
);
|
|
896
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
796
897
|
});
|
|
797
898
|
test('last saturday', async () => {
|
|
798
899
|
nowIs('2023-01-03 00:00:00');
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
900
|
+
const rangeQuery = mkRangeQuery(
|
|
901
|
+
"f'last saturday'",
|
|
902
|
+
'2022-12-31 00:00:00',
|
|
903
|
+
'2023-01-01 00:00:00'
|
|
904
|
+
);
|
|
905
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
803
906
|
});
|
|
804
907
|
test('next sunday', async () => {
|
|
805
908
|
nowIs('2023-01-03 00:00:00');
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
909
|
+
const rangeQuery = mkRangeQuery(
|
|
910
|
+
"f'next sunday'",
|
|
911
|
+
'2023-01-08 00:00:00',
|
|
912
|
+
'2023-01-09 00:00:00'
|
|
913
|
+
);
|
|
914
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
810
915
|
});
|
|
811
916
|
test('next monday', async () => {
|
|
812
917
|
nowIs('2023-01-03 00:00:00');
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
918
|
+
const rangeQuery = mkRangeQuery(
|
|
919
|
+
"f'next monday'",
|
|
920
|
+
'2023-01-09 00:00:00',
|
|
921
|
+
'2023-01-10 00:00:00'
|
|
922
|
+
);
|
|
923
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
817
924
|
});
|
|
818
925
|
test('next tuesday', async () => {
|
|
819
926
|
nowIs('2023-01-03 00:00:00');
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
927
|
+
const rangeQuery = mkRangeQuery(
|
|
928
|
+
"f'next tuesday'",
|
|
929
|
+
'2023-01-10 00:00:00',
|
|
930
|
+
'2023-01-11 00:00:00'
|
|
931
|
+
);
|
|
932
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
824
933
|
});
|
|
825
934
|
test('next wednesday', async () => {
|
|
826
935
|
nowIs('2023-01-03 00:00:00');
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
936
|
+
const rangeQuery = mkRangeQuery(
|
|
937
|
+
"f'next wednesday'",
|
|
938
|
+
'2023-01-04 00:00:00',
|
|
939
|
+
'2023-01-05 00:00:00'
|
|
940
|
+
);
|
|
941
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
831
942
|
});
|
|
832
943
|
test('next thursday', async () => {
|
|
833
944
|
nowIs('2023-01-03 00:00:00');
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
945
|
+
const rangeQuery = mkRangeQuery(
|
|
946
|
+
"f'next thursday'",
|
|
947
|
+
'2023-01-05 00:00:00',
|
|
948
|
+
'2023-01-06 00:00:00'
|
|
949
|
+
);
|
|
950
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
838
951
|
});
|
|
839
952
|
test('next friday', async () => {
|
|
840
953
|
nowIs('2023-01-03 00:00:00');
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
954
|
+
const rangeQuery = mkRangeQuery(
|
|
955
|
+
"f'next friday'",
|
|
956
|
+
'2023-01-06 00:00:00',
|
|
957
|
+
'2023-01-07 00:00:00'
|
|
958
|
+
);
|
|
959
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
845
960
|
});
|
|
846
961
|
test('next saturday', async () => {
|
|
847
962
|
nowIs('2023-01-03 00:00:00');
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
963
|
+
const rangeQuery = mkRangeQuery(
|
|
964
|
+
"f'next Saturday'",
|
|
965
|
+
'2023-01-07 00:00:00',
|
|
966
|
+
'2023-01-08 00:00:00'
|
|
967
|
+
);
|
|
968
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
852
969
|
});
|
|
853
970
|
test('temporal filters are case insensitive', async () => {
|
|
854
971
|
nowIs('2023-01-03 00:00:00');
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
972
|
+
const rangeQuery = mkRangeQuery(
|
|
973
|
+
"f'Null Or noT aFter TomoRRow'",
|
|
974
|
+
'2023-01-04 00:00:00',
|
|
975
|
+
'2023-01-05 00:00:00'
|
|
976
|
+
);
|
|
977
|
+
await expect(rangeQuery).matchesRows(
|
|
978
|
+
db,
|
|
859
979
|
{n: 'before'},
|
|
860
980
|
{n: 'first'},
|
|
861
981
|
{n: 'last'},
|
|
862
982
|
{n: 'z-null'}
|
|
863
983
|
);
|
|
864
984
|
});
|
|
985
|
+
const tzTesting = dbName !== 'presto' && dbName !== 'trino';
|
|
986
|
+
describe('query time zone', () => {
|
|
987
|
+
test.when(tzTesting)('day literal in query time zone', async () => {
|
|
988
|
+
const rangeQuery = mkRangeQuery(
|
|
989
|
+
"f'2024-01-01'",
|
|
990
|
+
'2024-01-01 00:00:00',
|
|
991
|
+
'2024-01-02 00:00:00',
|
|
992
|
+
'America/Mexico_City'
|
|
993
|
+
);
|
|
994
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
995
|
+
});
|
|
996
|
+
test.when(tzTesting)('day literal in query time zone', async () => {
|
|
997
|
+
nowIs('2024-01-15 00:34:56', 'America/Mexico_City');
|
|
998
|
+
const rangeQuery = mkRangeQuery(
|
|
999
|
+
"f'today'",
|
|
1000
|
+
'2024-01-15 00:00:00',
|
|
1001
|
+
'2024-01-16 00:00:00',
|
|
1002
|
+
'America/Mexico_City'
|
|
1003
|
+
);
|
|
1004
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
1005
|
+
});
|
|
1006
|
+
test.when(tzTesting)('day literal in query time zone', async () => {
|
|
1007
|
+
nowIs('2024-01-01 00:00:00', 'America/Mexico_City');
|
|
1008
|
+
const rangeQuery = mkRangeQuery(
|
|
1009
|
+
"f'next wednesday'",
|
|
1010
|
+
'2024-01-03 00:00:00',
|
|
1011
|
+
'2024-01-04 00:00:00',
|
|
1012
|
+
'America/Mexico_City'
|
|
1013
|
+
);
|
|
1014
|
+
await expect(rangeQuery).malloyResultMatches(db, inRange);
|
|
1015
|
+
});
|
|
1016
|
+
test.when(tzTesting)('day literal in query time zone', async () => {
|
|
1017
|
+
const exactTimeModel = mkEqTime('2024-01-15 12:00:00');
|
|
1018
|
+
await expect(`
|
|
1019
|
+
run: eqtime -> {
|
|
1020
|
+
timezone: 'America/Mexico_City'
|
|
1021
|
+
where: t ~ f'2024-01-15 06:00:00' // 6 AM Mexico City = Noon UTC
|
|
1022
|
+
select: t, n
|
|
1023
|
+
}
|
|
1024
|
+
`).malloyResultMatches(exactTimeModel, {n: 'exact'});
|
|
1025
|
+
});
|
|
1026
|
+
});
|
|
865
1027
|
});
|
|
866
1028
|
});
|
|
@@ -57,6 +57,57 @@ function getSplitFunction(db: string) {
|
|
|
57
57
|
}[db];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function lotsOfNumbersSQLTable(db: string): string | undefined {
|
|
61
|
+
return {
|
|
62
|
+
'mysql': `
|
|
63
|
+
SELECT
|
|
64
|
+
p0.n
|
|
65
|
+
+ p1.n*2
|
|
66
|
+
+ p2.n * POWER(2,2)
|
|
67
|
+
+ p3.n * POWER(2,3)
|
|
68
|
+
+ p4.n * POWER(2,4)
|
|
69
|
+
+ p5.n * POWER(2,5)
|
|
70
|
+
+ p6.n * POWER(2,6)
|
|
71
|
+
+ p7.n * POWER(2,7)
|
|
72
|
+
+ p8.n * POWER(2,8)
|
|
73
|
+
+ p9.n * POWER(2,9)
|
|
74
|
+
+ p10.n * POWER(2,10)
|
|
75
|
+
+ p11.n * POWER(2,11)
|
|
76
|
+
+ p12.n * POWER(2,12)
|
|
77
|
+
+ p13.n * POWER(2,13)
|
|
78
|
+
+ p14.n * POWER(2,14)
|
|
79
|
+
+ p15.n * POWER(2,15)
|
|
80
|
+
+ p16.n * POWER(2,16)
|
|
81
|
+
+ p17.n * POWER(2,17)
|
|
82
|
+
+ p18.n * POWER(2,18)
|
|
83
|
+
+ p19.n * POWER(2,19)
|
|
84
|
+
as n
|
|
85
|
+
FROM
|
|
86
|
+
(SELECT 0 as n UNION SELECT 1) p0,
|
|
87
|
+
(SELECT 0 as n UNION SELECT 1) p1,
|
|
88
|
+
(SELECT 0 as n UNION SELECT 1) p2,
|
|
89
|
+
(SELECT 0 as n UNION SELECT 1) p3,
|
|
90
|
+
(SELECT 0 as n UNION SELECT 1) p4,
|
|
91
|
+
(SELECT 0 as n UNION SELECT 1) p5,
|
|
92
|
+
(SELECT 0 as n UNION SELECT 1) p6,
|
|
93
|
+
(SELECT 0 as n UNION SELECT 1) p7,
|
|
94
|
+
(SELECT 0 as n UNION SELECT 1) p8,
|
|
95
|
+
(SELECT 0 as n UNION SELECT 1) p9,
|
|
96
|
+
(SELECT 0 as n UNION SELECT 1) p10,
|
|
97
|
+
(SELECT 0 as n UNION SELECT 1) p11,
|
|
98
|
+
(SELECT 0 as n UNION SELECT 1) p12,
|
|
99
|
+
(SELECT 0 as n UNION SELECT 1) p13,
|
|
100
|
+
(SELECT 0 as n UNION SELECT 1) p14,
|
|
101
|
+
(SELECT 0 as n UNION SELECT 1) p15,
|
|
102
|
+
(SELECT 0 as n UNION SELECT 1) p16,
|
|
103
|
+
(SELECT 0 as n UNION SELECT 1) p17,
|
|
104
|
+
(SELECT 0 as n UNION SELECT 1) p18,
|
|
105
|
+
(SELECT 0 as n UNION SELECT 1) p19
|
|
106
|
+
`,
|
|
107
|
+
'duckdb': 'SELECT UNNEST(GENERATE_SERIES(0,1048575,1)) as n',
|
|
108
|
+
}[db];
|
|
109
|
+
}
|
|
110
|
+
|
|
60
111
|
afterAll(async () => {
|
|
61
112
|
await runtimes.closeAll();
|
|
62
113
|
});
|
|
@@ -64,6 +115,25 @@ afterAll(async () => {
|
|
|
64
115
|
runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
65
116
|
const q = runtime.getQuoter();
|
|
66
117
|
|
|
118
|
+
const lotsSQL = lotsOfNumbersSQLTable(databaseName);
|
|
119
|
+
it.when(lotsSQL !== undefined)(
|
|
120
|
+
`big symmetric sum - ${databaseName}`,
|
|
121
|
+
async () => {
|
|
122
|
+
await expect(`
|
|
123
|
+
source: lots_of_numbers is ${databaseName}.sql(""" ${lotsSQL} """) extend {
|
|
124
|
+
measure:
|
|
125
|
+
total_n is n.sum()
|
|
126
|
+
}
|
|
127
|
+
query: two_rows is ${databaseName}.table('malloytest.state_facts') -> {select: state; limit: 2}
|
|
128
|
+
source: b is two_rows extend {
|
|
129
|
+
join_cross: lots_of_numbers
|
|
130
|
+
}
|
|
131
|
+
run: b -> {
|
|
132
|
+
aggregate: lots_of_numbers.total_n
|
|
133
|
+
}
|
|
134
|
+
`).malloyResultMatches(runtime, {total_n: 549755289600});
|
|
135
|
+
}
|
|
136
|
+
);
|
|
67
137
|
// Issue: #1284
|
|
68
138
|
it(`parenthesize output field values - ${databaseName}`, async () => {
|
|
69
139
|
await expect(`
|
|
@@ -278,23 +348,27 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
278
348
|
await expect(`
|
|
279
349
|
source: a is ${databaseName}.table('malloytest.airports') extend {
|
|
280
350
|
primary_key: code
|
|
281
|
-
dimension:
|
|
351
|
+
dimension:
|
|
352
|
+
big_elevation is elevation * 100000.0
|
|
353
|
+
little_elevation is elevation / 100000.0
|
|
282
354
|
measure:
|
|
283
355
|
total_elevation is elevation.sum()
|
|
356
|
+
total_little_elevation is round(little_elevation.sum(),4)
|
|
284
357
|
average_elevation is floor(elevation.avg())
|
|
285
|
-
total_big_elevation is big_elevation.sum()
|
|
358
|
+
total_big_elevation is round(big_elevation.sum(),0) // mysql is weird
|
|
286
359
|
average_big_elevation is floor(big_elevation.avg())
|
|
287
360
|
}
|
|
288
361
|
query: two_rows is ${databaseName}.table('malloytest.state_facts') -> {select: state; limit: 2}
|
|
289
362
|
source: b is two_rows extend {
|
|
290
|
-
join_cross: a
|
|
363
|
+
join_cross: a
|
|
291
364
|
}
|
|
292
365
|
|
|
293
|
-
run: b -> {aggregate: a.total_elevation, a.average_elevation, a.total_big_elevation, a.average_big_elevation}
|
|
366
|
+
run: b -> {aggregate: a.total_elevation, a.total_little_elevation, a.average_elevation, a.total_big_elevation, a.average_big_elevation}
|
|
294
367
|
// run: two_rows
|
|
295
368
|
|
|
296
369
|
`).malloyResultMatches(runtime, {
|
|
297
370
|
total_elevation: 22629146,
|
|
371
|
+
total_little_elevation: 226.2915,
|
|
298
372
|
average_elevation: 1143,
|
|
299
373
|
total_big_elevation: 2262914600000,
|
|
300
374
|
average_big_elevation: 114329035,
|