@malloydata/malloy-tests 0.0.149 → 0.0.150

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,13 +21,13 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@jest/globals": "^29.4.3",
24
- "@malloydata/db-bigquery": "^0.0.149",
25
- "@malloydata/db-duckdb": "^0.0.149",
26
- "@malloydata/db-postgres": "^0.0.149",
27
- "@malloydata/db-snowflake": "^0.0.149",
28
- "@malloydata/db-trino": "^0.0.149",
29
- "@malloydata/malloy": "^0.0.149",
30
- "@malloydata/render": "^0.0.149",
24
+ "@malloydata/db-bigquery": "^0.0.150",
25
+ "@malloydata/db-duckdb": "^0.0.150",
26
+ "@malloydata/db-postgres": "^0.0.150",
27
+ "@malloydata/db-snowflake": "^0.0.150",
28
+ "@malloydata/db-trino": "^0.0.150",
29
+ "@malloydata/malloy": "^0.0.150",
30
+ "@malloydata/render": "^0.0.150",
31
31
  "jsdom": "^22.1.0",
32
32
  "luxon": "^2.4.0",
33
33
  "madge": "^6.0.0"
@@ -36,5 +36,5 @@
36
36
  "@types/jsdom": "^21.1.1",
37
37
  "@types/luxon": "^2.4.0"
38
38
  },
39
- "version": "0.0.149"
39
+ "version": "0.0.150"
40
40
  }
@@ -97,19 +97,20 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
97
97
  });
98
98
 
99
99
  // bigquery doesn't support row count based sampling.
100
- test.when(databaseName !== 'bigquery' && databaseName !== 'trino')(
101
- `index rows count - ${databaseName}`,
102
- async () => {
103
- await expect(`
100
+ test.when(
101
+ databaseName !== 'bigquery' &&
102
+ databaseName !== 'trino' &&
103
+ databaseName !== 'presto'
104
+ )(`index rows count - ${databaseName}`, async () => {
105
+ await expect(`
104
106
  run: ${databaseName}.table('malloytest.state_facts') extend {
105
107
  dimension: one is 'one'
106
108
  } -> {index:one, state; sample: 10 }
107
109
  -> {select: fieldName, weight, fieldValue; order_by: 2 desc; where: fieldName = 'one'}
108
110
  `).malloyResultMatches(runtime, {fieldName: 'one', weight: 10});
109
- }
110
- );
111
+ });
111
112
 
112
- it.when(databaseName !== 'trino')(
113
+ it.when(databaseName !== 'trino' && databaseName !== 'presto')(
113
114
  `index rows count - ${databaseName}`,
114
115
  async () => {
115
116
  await expect(`
@@ -112,32 +112,32 @@ expressionModels.forEach((expressionModel, databaseName) => {
112
112
  };
113
113
 
114
114
  describe('concat', () => {
115
- it.when(!brokenIn('trino', databaseName) /* crswenson */)(
116
- `works - ${databaseName}`,
117
- async () => {
118
- const expected = {
119
- 'bigquery': 'foo2003-01-01 12:00:00+00',
120
- 'snowflake': 'foo2003-01-01T12:00:00.000Z',
121
- };
122
-
123
- await funcTestMultiple(
124
- ["concat('foo', 'bar')", 'foobar'],
125
- ["concat(1, 'bar')", '1bar'],
126
- [
127
- "concat('cons', true)",
128
- databaseName === 'postgres' ? 'const' : 'construe',
129
- ],
130
- ["concat('foo', @2003)", 'foo2003-01-01'],
131
- [
132
- "concat('foo', @2003-01-01 12:00:00)",
133
- expected[databaseName] ?? 'foo2003-01-01 12:00:00',
134
- ],
135
- // TODO Maybe implement consistent null behavior
136
- // ["concat('foo', null)", null],
137
- ['concat()', '']
138
- );
139
- }
140
- );
115
+ it.when(
116
+ !brokenIn('trino', databaseName) &&
117
+ !brokenIn('presto', databaseName) /* crswenson */
118
+ )(`works - ${databaseName}`, async () => {
119
+ const expected = {
120
+ 'bigquery': 'foo2003-01-01 12:00:00+00',
121
+ 'snowflake': 'foo2003-01-01T12:00:00.000Z',
122
+ };
123
+
124
+ await funcTestMultiple(
125
+ ["concat('foo', 'bar')", 'foobar'],
126
+ ["concat(1, 'bar')", '1bar'],
127
+ [
128
+ "concat('cons', true)",
129
+ databaseName === 'postgres' ? 'const' : 'construe',
130
+ ],
131
+ ["concat('foo', @2003)", 'foo2003-01-01'],
132
+ [
133
+ "concat('foo', @2003-01-01 12:00:00)",
134
+ expected[databaseName] ?? 'foo2003-01-01 12:00:00',
135
+ ],
136
+ // TODO Maybe implement consistent null behavior
137
+ // ["concat('foo', null)", null],
138
+ ['concat()', '']
139
+ );
140
+ });
141
141
  });
142
142
 
143
143
  describe('round', () => {
@@ -219,13 +219,15 @@ expressionModels.forEach((expressionModel, databaseName) => {
219
219
  "replace('axbxc', r'(a).(b).(c)', '\\\\0 - \\\\1 - \\\\2 - \\\\3')",
220
220
  databaseName === 'postgres'
221
221
  ? '\\0 - a - b - c'
222
- : databaseName === 'trino'
222
+ : databaseName === 'trino' || databaseName === 'presto'
223
223
  ? '0 - 1 - 2 - 3'
224
224
  : 'axbxc - a - b - c',
225
225
  ],
226
226
  [
227
227
  "replace('aaaa', '', 'c')",
228
- databaseName === 'trino' ? 'cacacacac' : 'aaaa',
228
+ databaseName === 'trino' || databaseName === 'presto'
229
+ ? 'cacacacac'
230
+ : 'aaaa',
229
231
  ],
230
232
  ["replace(null, 'a', 'c')", null],
231
233
  ["replace('aaaa', null, 'c')", null],
@@ -262,7 +264,8 @@ expressionModels.forEach((expressionModel, databaseName) => {
262
264
 
263
265
  describe('stddev', () => {
264
266
  // TODO symmetric aggregates don't work with custom aggregate functions in BQ currently
265
- if (['bigquery', 'snowflake', 'trino'].includes(databaseName)) return;
267
+ if (['bigquery', 'snowflake', 'trino', 'presto'].includes(databaseName))
268
+ return;
266
269
  it(`works - ${databaseName}`, async () => {
267
270
  await funcTestAgg('round(stddev(aircraft_models.seats))', 29);
268
271
  });
@@ -786,7 +789,7 @@ expressionModels.forEach((expressionModel, databaseName) => {
786
789
  });
787
790
  });
788
791
  describe('is_inf', () => {
789
- const inf = ['trino'].includes(databaseName)
792
+ const inf = ['trino', 'presto'].includes(databaseName)
790
793
  ? 'infinity!()'
791
794
  : "'+inf'::number";
792
795
  it(`works - ${databaseName}`, async () => {
@@ -1327,7 +1330,8 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
1327
1330
 
1328
1331
  it(`works with fanout and order_by - ${databaseName}`, async () => {
1329
1332
  // TODO bigquery cannot handle both fanout and order_by today
1330
- if (['bigquery', 'snowflake', 'trino'].includes(databaseName)) return;
1333
+ if (['bigquery', 'snowflake', 'trino', 'presto'].includes(databaseName))
1334
+ return;
1331
1335
  await expect(`##! experimental.aggregate_order_by
1332
1336
  run: state_facts extend { join_many:
1333
1337
  state_facts2 is ${databaseName}.table('malloytest.state_facts')
@@ -194,10 +194,10 @@ describe('join expression tests', () => {
194
194
  `).malloyResultMatches(joinModel, {f_sum2: 60462});
195
195
  });
196
196
 
197
- test.when(runtime.supportsNesting)(
198
- `model: unnest is left join - ${database}`,
199
- async () => {
200
- await expect(`
197
+ test.when(
198
+ runtime.supportsNesting && runtime.dialect.supportsLeftJoinUnnest
199
+ )(`model: unnest is left join - ${database}`, async () => {
200
+ await expect(`
201
201
  // produce a table with 4 rows that has a nested element
202
202
  query: a_states is ${database}.table('malloytest.state_facts')-> {
203
203
  where: state ? ~ 'A%'
@@ -220,8 +220,7 @@ describe('join expression tests', () => {
220
220
  limit: 5
221
221
  }
222
222
  `).malloyResultMatches(joinModel, [{}, {}, {}, {}, {}]);
223
- }
224
- );
223
+ });
225
224
 
226
225
  // not sure how to solve this one yet, just check for > 4 rows
227
226
  it(`All joins at the same level - ${database}`, async () => {
@@ -482,11 +482,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
482
482
  expect(q.sql.toLowerCase()).not.toContain('distinct');
483
483
  });
484
484
 
485
- it(`leafy nested count - ${databaseName}`, async () => {
486
- // in a joined table when the joined is leafiest
487
- // we need to make sure we don't count rows that
488
- // don't match the join.
489
- await expect(`
485
+ it.when(runtime.dialect.supportsLeftJoinUnnest)(
486
+ `leafy nested count - ${databaseName}`,
487
+ async () => {
488
+ // in a joined table when the joined is leafiest
489
+ // we need to make sure we don't count rows that
490
+ // don't match the join.
491
+ await expect(`
490
492
  source: am_states is ${databaseName}.table('malloytest.state_facts') -> {
491
493
  group_by: state,popular_name
492
494
  where: state ~ r'^(A|M)'
@@ -498,7 +500,6 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
498
500
  source: states is ${databaseName}.table('malloytest.state_facts') extend {
499
501
  join_many: am_states on state=am_states.state
500
502
  }
501
-
502
503
  run: states -> {
503
504
  where: state = 'CA'
504
505
  group_by:
@@ -509,12 +510,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
509
510
  root_count is count()
510
511
  }
511
512
  `).malloyResultMatches(runtime, {
512
- leafy_count: 0,
513
- root_count: 1,
514
- state: 'CA',
515
- am_state: null,
516
- });
517
- });
513
+ leafy_count: 0,
514
+ root_count: 1,
515
+ state: 'CA',
516
+ am_state: null,
517
+ });
518
+ }
519
+ );
518
520
 
519
521
  it(`basic index - ${databaseName}`, async () => {
520
522
  // Make sure basic indexing works.
@@ -560,7 +562,6 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
560
562
  }`).malloyResultMatches(runtime, {a: 2});
561
563
  });
562
564
 
563
-
564
565
  // average should only include non-null values in the denominator
565
566
  it(`avg ignore null- ${databaseName}`, async () => {
566
567
  await expect(`
@@ -1055,11 +1056,23 @@ SELECT row_to_json(finalStage) as row FROM __stage0 AS finalStage`);
1055
1056
  );
1056
1057
 
1057
1058
  test.when(runtime.supportsNesting && runtime.dialect.readsNestedData)(
1058
- `can unnest from file - ${databaseName}`,
1059
+ `can unnest simply from file - ${databaseName}`,
1059
1060
  async () => {
1060
1061
  await expect(`
1061
1062
  source: ga_sample is ${databaseName}.table('malloytest.ga_sample')
1063
+ run: ga_sample -> {
1064
+ aggregate:
1065
+ h is hits.count()
1066
+ }
1067
+ `).malloyResultMatches(runtime, {h: 13233});
1068
+ }
1069
+ );
1062
1070
 
1071
+ test.when(runtime.supportsNesting && runtime.dialect.readsNestedData)(
1072
+ `can unnest from file - ${databaseName}`,
1073
+ async () => {
1074
+ await expect(`
1075
+ source: ga_sample is ${databaseName}.table('malloytest.ga_sample')
1063
1076
  run: ga_sample -> {
1064
1077
  where: hits.product.productBrand != null
1065
1078
  group_by:
@@ -462,18 +462,17 @@ describe.each(runtimes.runtimeList)('%s date and time', (dbName, runtime) => {
462
462
  });
463
463
  });
464
464
 
465
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
466
- 'dependant join dialect fragments',
467
- async () => {
468
- await expect(`
465
+ test.when(
466
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
467
+ )('dependant join dialect fragments', async () => {
468
+ await expect(`
469
469
  source: timeData is ${dbName}.sql("""${timeSQL}""")
470
470
  run: timeData -> {
471
471
  extend: {join_one: joined is timeData on t_date = joined.t_date}
472
472
  group_by: t_month is joined.t_timestamp.month
473
473
  }
474
474
  `).malloyResultMatches(runtime, {t_month: new Date('2021-02-01')});
475
- }
476
- );
475
+ });
477
476
 
478
477
  describe('timezone set correctly', () => {
479
478
  test('timezone set in source used by query', async () => {
@@ -621,63 +620,60 @@ const utc_2020 = LuxonDateTime.fromObject(
621
620
  );
622
621
 
623
622
  describe.each(runtimes.runtimeList)('%s: tz literals', (dbName, runtime) => {
624
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
625
- `${dbName} - default timezone is UTC`,
626
- async () => {
627
- // this makes sure that the tests which use the test timezome are actually
628
- // testing something ... file this under "abundance of caution". It
629
- // really tests nothing, but I feel calmer with this here.
630
- const query = runtime.loadQuery(
631
- `
623
+ test.when(
624
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
625
+ )(`${dbName} - default timezone is UTC`, async () => {
626
+ // this makes sure that the tests which use the test timezome are actually
627
+ // testing something ... file this under "abundance of caution". It
628
+ // really tests nothing, but I feel calmer with this here.
629
+ const query = runtime.loadQuery(
630
+ `
632
631
  run: ${dbName}.sql("SELECT 1 as one") -> {
633
632
  group_by: literal_time is @2020-02-20 00:00:00
634
633
  }
635
634
  `
636
- );
637
- const result = await query.run();
638
- const literal = result.data.path(0, 'literal_time').value as Date;
639
- const have = LuxonDateTime.fromJSDate(literal);
640
- expect(have.valueOf()).toEqual(utc_2020.valueOf());
641
- }
642
- );
643
-
644
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
645
- 'literal with zone name',
646
- async () => {
647
- const query = runtime.loadQuery(
648
- `
635
+ );
636
+ const result = await query.run();
637
+ const literal = result.data.path(0, 'literal_time').value as Date;
638
+ const have = LuxonDateTime.fromJSDate(literal);
639
+ expect(have.valueOf()).toEqual(utc_2020.valueOf());
640
+ });
641
+
642
+ test.when(
643
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
644
+ )('literal with zone name', async () => {
645
+ const query = runtime.loadQuery(
646
+ `
649
647
  run: ${dbName}.sql("SELECT 1 as one") -> {
650
648
  group_by: literal_time is @2020-02-20 00:00:00[America/Mexico_City]
651
649
  }
652
650
  `
653
- );
654
- const result = await query.run();
655
- const literal = result.data.path(0, 'literal_time').value as Date;
656
- const have = LuxonDateTime.fromJSDate(literal);
657
- expect(have.valueOf()).toEqual(zone_2020.valueOf());
658
- }
659
- );
651
+ );
652
+ const result = await query.run();
653
+ const literal = result.data.path(0, 'literal_time').value as Date;
654
+ const have = LuxonDateTime.fromJSDate(literal);
655
+ expect(have.valueOf()).toEqual(zone_2020.valueOf());
656
+ });
660
657
  });
661
658
 
662
659
  describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
663
660
  const q = runtime.getQuoter();
664
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
665
- 'literal timestamps',
666
- async () => {
667
- const query = runtime.loadQuery(
668
- `
661
+ test.when(
662
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
663
+ )('literal timestamps', async () => {
664
+ const query = runtime.loadQuery(
665
+ `
669
666
  run: ${dbName}.sql("SELECT 1 as one") -> {
670
667
  timezone: '${zone}'
671
668
  group_by: literal_time is @2020-02-20 00:00:00
672
669
  }
673
670
  `
674
- );
675
- const result = await query.run();
676
- const literal = result.data.path(0, 'literal_time').value as Date;
677
- const have = LuxonDateTime.fromJSDate(literal);
678
- expect(have.valueOf()).toEqual(zone_2020.valueOf());
679
- }
680
- );
671
+ );
672
+ const result = await query.run();
673
+ const literal = result.data.path(0, 'literal_time').value as Date;
674
+ const have = LuxonDateTime.fromJSDate(literal);
675
+ expect(have.valueOf()).toEqual(zone_2020.valueOf());
676
+ });
681
677
 
682
678
  test('extract', async () => {
683
679
  await expect(
@@ -691,7 +687,9 @@ describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
691
687
  ).malloyResultMatches(runtime, {mex_midnight: 18, mex_day: 19});
692
688
  });
693
689
 
694
- test.when(!brokenIn('trino', dbName) /* mtoy */)('truncate day', async () => {
690
+ test.when(
691
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
692
+ )('truncate day', async () => {
695
693
  // At midnight in london it the 19th in Mexico, so that truncates to
696
694
  // midnight on the 19th
697
695
  const mex_19 = LuxonDateTime.fromISO('2020-02-19T00:00:00', {zone});
@@ -704,32 +702,30 @@ describe.each(runtimes.runtimeList)('%s: query tz', (dbName, runtime) => {
704
702
  ).malloyResultMatches(runtime, {mex_day: mex_19.toJSDate()});
705
703
  });
706
704
 
707
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
708
- 'cast timestamp to date',
709
- async () => {
710
- // At midnight in london it is the 19th in Mexico, so when we cast that
711
- // to a date, it should be the 19th.
712
- await expect(
713
- `run: ${dbName}.sql("SELECT 1 as x") -> {
705
+ test.when(
706
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
707
+ )('cast timestamp to date', async () => {
708
+ // At midnight in london it is the 19th in Mexico, so when we cast that
709
+ // to a date, it should be the 19th.
710
+ await expect(
711
+ `run: ${dbName}.sql("SELECT 1 as x") -> {
714
712
  timezone: '${zone}'
715
713
  extend: { dimension: utc_midnight is @2020-02-20 00:00:00[UTC] }
716
714
  select: mex_day is day(utc_midnight::date)
717
715
  }`
718
- ).malloyResultMatches(runtime, {mex_day: 19});
719
- }
720
- );
721
-
722
- test.when(!brokenIn('trino', dbName) /* mtoy */)(
723
- 'cast date to timestamp',
724
- async () => {
725
- await expect(
726
- `run: ${dbName}.sql(""" SELECT DATE '2020-02-20' AS ${q`mex_20`} """) -> {
716
+ ).malloyResultMatches(runtime, {mex_day: 19});
717
+ });
718
+
719
+ test.when(
720
+ !brokenIn('trino', dbName) && !brokenIn('presto', dbName) /* mtoy */
721
+ )('cast date to timestamp', async () => {
722
+ await expect(
723
+ `run: ${dbName}.sql(""" SELECT DATE '2020-02-20' AS ${q`mex_20`} """) -> {
727
724
  timezone: '${zone}'
728
725
  select: mex_ts is mex_20::timestamp
729
726
  }`
730
- ).malloyResultMatches(runtime, {mex_ts: zone_2020.toJSDate()});
731
- }
732
- );
727
+ ).malloyResultMatches(runtime, {mex_ts: zone_2020.toJSDate()});
728
+ });
733
729
  });
734
730
 
735
731
  afterAll(async () => {