@malloydata/malloy-tests 0.0.196-dev241007190115 → 0.0.196-dev241007230836

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.
Binary file
@@ -0,0 +1,32 @@
1
+ #! /bin/bash
2
+ rm -rf .tmp
3
+ mkdir .tmp
4
+
5
+ #
6
+
7
+ # run docker
8
+ docker run -p 3306:3306 -d -v $PWD/../data/mysql:/init_data --name mysql-malloy -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql:8.4.2
9
+
10
+ # wait for server to start
11
+ counter=0
12
+ while ! docker logs mysql-malloy 2>&1 | grep -q "mysqld: ready for connections"
13
+ do
14
+ sleep 10
15
+ counter=$((counter+1))
16
+ # if doesn't start after 2 minutes, output logs and kill process
17
+ if [ $counter -eq 120 ]
18
+ then
19
+ docker logs mysql-malloy >& ./.tmp/mysql-malloy.logs
20
+ docker rm -f mysql-malloy
21
+ echo "MySQL did not start successfully, check .tmp/mysql-malloy.logs"
22
+ exit 1
23
+ break
24
+ fi
25
+ done
26
+
27
+ # load the test data.
28
+ docker exec mysql-malloy cp /init_data/malloytest.mysql.gz /tmp
29
+ docker exec mysql-malloy gunzip /tmp/malloytest.mysql.gz
30
+ docker exec mysql-malloy mysql -uroot -e 'drop database if exists malloytest; create database malloytest; use malloytest; source /tmp/malloytest.mysql'
31
+
32
+ echo "MySQL running on port 3306"
@@ -0,0 +1,7 @@
1
+ #! /bin/bash
2
+
3
+ # clear tmp files
4
+ rm -rf .tmp
5
+
6
+ # stop container
7
+ docker rm -f mysql-malloy
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.196-dev241007190115",
25
- "@malloydata/db-duckdb": "^0.0.196-dev241007190115",
26
- "@malloydata/db-postgres": "^0.0.196-dev241007190115",
27
- "@malloydata/db-snowflake": "^0.0.196-dev241007190115",
28
- "@malloydata/db-trino": "^0.0.196-dev241007190115",
29
- "@malloydata/malloy": "^0.0.196-dev241007190115",
30
- "@malloydata/render": "^0.0.196-dev241007190115",
24
+ "@malloydata/db-bigquery": "^0.0.196-dev241007230836",
25
+ "@malloydata/db-duckdb": "^0.0.196-dev241007230836",
26
+ "@malloydata/db-postgres": "^0.0.196-dev241007230836",
27
+ "@malloydata/db-snowflake": "^0.0.196-dev241007230836",
28
+ "@malloydata/db-trino": "^0.0.196-dev241007230836",
29
+ "@malloydata/malloy": "^0.0.196-dev241007230836",
30
+ "@malloydata/render": "^0.0.196-dev241007230836",
31
31
  "events": "^3.3.0",
32
32
  "jsdom": "^22.1.0",
33
33
  "luxon": "^2.4.0",
@@ -37,5 +37,5 @@
37
37
  "@types/jsdom": "^21.1.1",
38
38
  "@types/luxon": "^2.4.0"
39
39
  },
40
- "version": "0.0.196-dev241007190115"
40
+ "version": "0.0.196-dev241007230836"
41
41
  }
@@ -24,7 +24,11 @@
24
24
 
25
25
  import {RuntimeList, allDatabases} from '../../runtimes';
26
26
  import '../../util/db-jest-matchers';
27
- import {databasesFromEnvironmentOr, mkSqlEqWith} from '../../util';
27
+ import {
28
+ booleanResult,
29
+ databasesFromEnvironmentOr,
30
+ mkSqlEqWith,
31
+ } from '../../util';
28
32
 
29
33
  const runtimes = new RuntimeList(databasesFromEnvironmentOr(allDatabases));
30
34
 
@@ -129,6 +133,7 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
129
133
  // simple turtle expressions
130
134
  it('simple turtle', async () => {
131
135
  await expect(`
136
+ // # test.debug
132
137
  run: ${databaseName}.table('malloytest.state_facts') -> {
133
138
  group_by: popular_name
134
139
  aggregate: airport_count.sum()
@@ -396,30 +401,28 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
396
401
  `).malloyResultMatches(expressionModel, {m_count: 63});
397
402
  });
398
403
 
404
+ const nativeInt = databaseName === 'mysql' ? 'signed' : 'integer';
399
405
  it('sql cast', async () => {
400
406
  await expect(`
401
407
  run: aircraft -> {
402
- group_by: a is "312"::"integer"
408
+ group_by: a is "312"::"${nativeInt}"
403
409
  }
404
410
  `).malloyResultMatches(expressionModel, {a: 312});
405
411
  });
406
412
 
407
- test.when(!['postgres'].includes(runtime.connection.name))(
408
- 'sql safe cast',
409
- async () => {
410
- await expect(`
413
+ test.when(runtime.dialect.supportsSafeCast)('sql safe cast', async () => {
414
+ await expect(`
411
415
  run: ${databaseName}.sql('SELECT 1 as one') -> { select:
412
416
  bad_date is '12a':::date
413
417
  bad_number is 'abc':::number
414
- good_number is "312":::"integer"
418
+ good_number is "312":::"${nativeInt}"
415
419
  }
416
420
  `).malloyResultMatches(expressionModel, {
417
- bad_date: null,
418
- bad_number: null,
419
- good_number: 312,
420
- });
421
- }
422
- );
421
+ bad_date: null,
422
+ bad_number: null,
423
+ good_number: 312,
424
+ });
425
+ });
423
426
 
424
427
  it('many_field.sum() has correct locality', async () => {
425
428
  await expect(`
@@ -494,8 +497,8 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
494
497
  group_by: boolean_2 is sql_boolean("\${engines} = 2")
495
498
  }
496
499
  `).malloyResultMatches(expressionModel, {
497
- boolean_1: true,
498
- boolean_2: false,
500
+ boolean_1: booleanResult(true, databaseName),
501
+ boolean_2: booleanResult(false, databaseName),
499
502
  });
500
503
  });
501
504
 
@@ -817,7 +820,10 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
817
820
  no_paren is false and fot
818
821
  paren is false and (fot)
819
822
  }`
820
- ).malloyResultMatches(runtime, {paren: false, no_paren: false});
823
+ ).malloyResultMatches(runtime, {
824
+ paren: booleanResult(false, databaseName),
825
+ no_paren: booleanResult(false, databaseName),
826
+ });
821
827
  });
822
828
  });
823
829
 
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import {RuntimeList, allDatabases} from '../../runtimes';
26
- import {brokenIn, databasesFromEnvironmentOr} from '../../util';
26
+ import {booleanResult, brokenIn, databasesFromEnvironmentOr} from '../../util';
27
27
  import '../../util/db-jest-matchers';
28
28
  import * as malloy from '@malloydata/malloy';
29
29
 
@@ -107,7 +107,7 @@ expressionModels.forEach((x, databaseName) => {
107
107
  return await expressionModel
108
108
  .loadQuery(
109
109
  `
110
- run: aircraft -> { ${testCases.map(
110
+ run: state_facts -> { ${testCases.map(
111
111
  (testCase, i) => `group_by: f${i} is ${testCase[0]}`
112
112
  )} }`
113
113
  )
@@ -137,7 +137,11 @@ expressionModels.forEach((x, databaseName) => {
137
137
  ["concat(1, 'bar')", '1bar'],
138
138
  [
139
139
  "concat('cons', true)",
140
- databaseName === 'postgres' ? 'const' : 'construe',
140
+ databaseName === 'postgres'
141
+ ? 'const'
142
+ : databaseName === 'mysql'
143
+ ? 'cons1'
144
+ : 'construe',
141
145
  ],
142
146
  ["concat('foo', @2003)", 'foo2003-01-01'],
143
147
  [
@@ -216,7 +220,7 @@ expressionModels.forEach((x, databaseName) => {
216
220
  ["regexp_extract('I have a dog', r'd[aeiou]g')", 'dog'],
217
221
  ["regexp_extract(null, r'd[aeiou]g')", null],
218
222
  ["regexp_extract('foo', null)", null],
219
- ["regexp_extract('I have a d0g', r'd\\dg')", 'd0g']
223
+ ["regexp_extract('I have a d0g', r'd.g')", 'd0g']
220
224
  );
221
225
  });
222
226
  });
@@ -230,7 +234,9 @@ expressionModels.forEach((x, databaseName) => {
230
234
  "replace('axbxc', r'(a).(b).(c)', '\\\\0 - \\\\1 - \\\\2 - \\\\3')",
231
235
  databaseName === 'postgres'
232
236
  ? '\\0 - a - b - c'
233
- : databaseName === 'trino' || databaseName === 'presto'
237
+ : databaseName === 'trino' ||
238
+ databaseName === 'presto' ||
239
+ databaseName === 'mysql'
234
240
  ? '0 - 1 - 2 - 3'
235
241
  : 'axbxc - a - b - c',
236
242
  ],
@@ -263,8 +269,8 @@ expressionModels.forEach((x, databaseName) => {
263
269
  describe('raw function call', () => {
264
270
  it(`works - ${databaseName}`, async () => {
265
271
  await funcTestMultiple(
266
- ['floor(cbrt!(27)::number)', 3],
267
- ['floor(cbrt!number(27))', 3],
272
+ ['floor(sqrt!(25)::number)', 5],
273
+ ['floor(sqrt!number(25))', 5],
268
274
  ["substr('foo bar baz', -3)", 'baz'],
269
275
  ["substr(nullif('x','x'), 1, 2)", null], // nullMatchesFunctionSignature
270
276
  ["substr('aaaa', null, 1)", null],
@@ -275,7 +281,11 @@ expressionModels.forEach((x, databaseName) => {
275
281
 
276
282
  describe('stddev', () => {
277
283
  // TODO symmetric aggregates don't work with custom aggregate functions in BQ currently
278
- if (['bigquery', 'snowflake', 'trino', 'presto'].includes(databaseName))
284
+ if (
285
+ ['bigquery', 'snowflake', 'trino', 'presto', 'mysql'].includes(
286
+ databaseName
287
+ )
288
+ )
279
289
  return;
280
290
  it(`works - ${databaseName}`, async () => {
281
291
  await funcTestAgg('round(stddev(aircraft_models.seats))', 29);
@@ -357,16 +367,35 @@ expressionModels.forEach((x, databaseName) => {
357
367
  expect(result.data.path(0, 'row_num').value).toBe(1);
358
368
  });
359
369
 
370
+ // should rework the tests to this form....
371
+ // it(`boolean type - ${databaseName}`, async () => {
372
+ // await expect(`
373
+ // # test.debug
374
+ // run: state_facts extend { join_one: airports on airports.state = state } -> {
375
+ // group_by: state
376
+ // nest: q is {
377
+ // group_by: airports.county
378
+ // calculate: row_num is row_number()
379
+ // }
380
+ // }
381
+ // `).malloyResultMatches(expressionModel, {
382
+ // big: 1,
383
+ // model_count: 58451,
384
+ // });
385
+ // });
386
+
360
387
  it(`works inside nest - ${databaseName}`, async () => {
361
388
  const result = await expressionModel
362
389
  .loadQuery(
363
- `run: state_facts extend { join_one: airports on airports.state = state } -> {
390
+ `
391
+ run: state_facts extend { join_one: airports on airports.state = state } -> {
364
392
  group_by: state
365
393
  nest: q is {
366
394
  group_by: airports.county
367
395
  calculate: row_num is row_number()
368
396
  }
369
- }`
397
+ }
398
+ `
370
399
  )
371
400
  .run();
372
401
  expect(result.data.path(0, 'q', 0, 'row_num').value).toBe(1);
@@ -582,8 +611,12 @@ expressionModels.forEach((x, databaseName) => {
582
611
  }`
583
612
  )
584
613
  .run();
585
- expect(result.data.path(0, 'lag_val').value).toBe(true);
586
- expect(result.data.path(1, 'lag_val').value).toBe(false);
614
+ expect(result.data.path(0, 'lag_val').value).toBe(
615
+ booleanResult(true, databaseName)
616
+ );
617
+ expect(result.data.path(1, 'lag_val').value).toBe(
618
+ booleanResult(false, databaseName)
619
+ );
587
620
  });
588
621
  });
589
622
 
@@ -803,7 +836,7 @@ expressionModels.forEach((x, databaseName) => {
803
836
  const inf = ['trino', 'presto'].includes(databaseName)
804
837
  ? 'infinity!()'
805
838
  : "'+inf'::number";
806
- it(`works - ${databaseName}`, async () => {
839
+ it.when(databaseName !== 'mysql')(`works - ${databaseName}`, async () => {
807
840
  await funcTestMultiple(
808
841
  [`is_inf(${inf})`, true],
809
842
  ['is_inf(100)', false],
@@ -812,7 +845,7 @@ expressionModels.forEach((x, databaseName) => {
812
845
  });
813
846
  });
814
847
  describe('is_nan', () => {
815
- it(`works - ${databaseName}`, async () => {
848
+ it.when(databaseName !== 'mysql')(`works - ${databaseName}`, async () => {
816
849
  await funcTestMultiple(
817
850
  ["is_nan('NaN'::number)", true],
818
851
  ['is_nan(100)', false],
@@ -824,10 +857,13 @@ expressionModels.forEach((x, databaseName) => {
824
857
  it(`works - ${databaseName}`, async () => {
825
858
  await funcTestMultiple(
826
859
  ['greatest(1, 10, -100)', 10],
827
- ['greatest(@2003, @2004, @1994) = @2004', true],
860
+ [
861
+ 'greatest(@2003, @2004, @1994) = @2004',
862
+ booleanResult(true, databaseName),
863
+ ],
828
864
  [
829
865
  'greatest(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:59:00',
830
- true,
866
+ booleanResult(true, databaseName),
831
867
  ],
832
868
  ["greatest('a', 'b')", 'b'],
833
869
  ['greatest(1, null, 0)', null],
@@ -839,10 +875,13 @@ expressionModels.forEach((x, databaseName) => {
839
875
  it(`works - ${databaseName}`, async () => {
840
876
  await funcTestMultiple(
841
877
  ['least(1, 10, -100)', -100],
842
- ['least(@2003, @2004, @1994) = @1994', true],
878
+ [
879
+ 'least(@2003, @2004, @1994) = @1994',
880
+ booleanResult(true, databaseName),
881
+ ],
843
882
  [
844
883
  'least(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:58:00',
845
- true,
884
+ booleanResult(true, databaseName),
846
885
  ],
847
886
  ["least('a', 'b')", 'a'],
848
887
  ['least(1, null, 0)', null],
@@ -872,25 +911,37 @@ expressionModels.forEach((x, databaseName) => {
872
911
  describe('starts_with', () => {
873
912
  it(`works - ${databaseName}`, async () => {
874
913
  await funcTestMultiple(
875
- ["starts_with('hello world', 'hello')", true],
876
- ["starts_with('hello world', 'world')", false],
877
- ["starts_with(null, 'world')", false],
878
- ["starts_with('hello world', null)", false]
914
+ [
915
+ "starts_with('hello world', 'hello')",
916
+ booleanResult(true, databaseName),
917
+ ],
918
+ [
919
+ "starts_with('hello world', 'world')",
920
+ booleanResult(false, databaseName),
921
+ ],
922
+ ["starts_with(null, 'world')", booleanResult(false, databaseName)],
923
+ ["starts_with('hello world', null)", booleanResult(false, databaseName)]
879
924
  );
880
925
  });
881
926
  });
882
927
  describe('ends_with', () => {
883
928
  it(`works - ${databaseName}`, async () => {
884
929
  await funcTestMultiple(
885
- ["ends_with('hello world', 'world')", true],
886
- ["ends_with('hello world', 'hello')", false],
887
- ["ends_with(null, 'world')", false],
888
- ["ends_with('hello world', null)", false]
930
+ [
931
+ "ends_with('hello world', 'world')",
932
+ booleanResult(true, databaseName),
933
+ ],
934
+ [
935
+ "ends_with('hello world', 'hello')",
936
+ booleanResult(false, databaseName),
937
+ ],
938
+ ["ends_with(null, 'world')", booleanResult(false, databaseName)],
939
+ ["ends_with('hello world', null)", booleanResult(false, databaseName)]
889
940
  );
890
941
  });
891
942
  });
892
943
  describe('trim', () => {
893
- it(`works - ${databaseName}`, async () => {
944
+ it(`trim works - ${databaseName}`, async () => {
894
945
  await funcTestMultiple(
895
946
  ["trim(' keep this ')", 'keep this'],
896
947
  ["trim('_ _keep_this_ _', '_ ')", 'keep_this'],
@@ -902,7 +953,7 @@ expressionModels.forEach((x, databaseName) => {
902
953
  });
903
954
  });
904
955
  describe('ltrim', () => {
905
- it(`works - ${databaseName}`, async () => {
956
+ it(`ltrim works - ${databaseName}`, async () => {
906
957
  await funcTestMultiple(
907
958
  ["ltrim(' keep this -> ')", 'keep this -> '],
908
959
  ["ltrim('_ _keep_this -> _ _', '_ ')", 'keep_this -> _ _'],
@@ -914,7 +965,7 @@ expressionModels.forEach((x, databaseName) => {
914
965
  });
915
966
  });
916
967
  describe('rtrim', () => {
917
- it(`works - ${databaseName}`, async () => {
968
+ it(`rtrim works - ${databaseName}`, async () => {
918
969
  await funcTestMultiple(
919
970
  ["rtrim(' <- keep this ')", ' <- keep this'],
920
971
  ["rtrim('_ _ <- keep_this_ _', '_ ')", '_ _ <- keep_this'],
@@ -930,12 +981,15 @@ expressionModels.forEach((x, databaseName) => {
930
981
  // There are around a billion values that rand() can be, so if this
931
982
  // test fails, most likely something is broken. Otherwise, you're the lucky
932
983
  // one in a billion!
933
- await funcTest('rand() = rand()', false);
984
+ await funcTest('rand() = rand()', booleanResult(false, databaseName));
934
985
  });
935
986
  });
936
987
  describe('pi', () => {
937
988
  it(`is pi - ${databaseName}`, async () => {
938
- await funcTest('abs(pi() - 3.141592653589793) < 0.0000000000001', true);
989
+ await funcTest(
990
+ 'abs(pi() - 3.141592653589793) < 0.0000000000001',
991
+ booleanResult(true, databaseName)
992
+ );
939
993
  });
940
994
  });
941
995
 
@@ -1098,8 +1152,8 @@ expressionModels.forEach((x, databaseName) => {
1098
1152
  aggregate: also_passes is abs(count_approx(airport_count)-count(airport_count))/count(airport_count) < 0.3
1099
1153
  }
1100
1154
  `).malloyResultMatches(runtime, {
1101
- 'passes': true,
1102
- 'also_passes': true,
1155
+ 'passes': booleanResult(true, databaseName),
1156
+ 'also_passes': booleanResult(true, databaseName),
1103
1157
  });
1104
1158
  });
1105
1159
  test.when(supported)('works with fanout', async () => {
@@ -1111,7 +1165,7 @@ expressionModels.forEach((x, databaseName) => {
1111
1165
  run: state_facts_fanout -> {
1112
1166
  aggregate: x is state_facts.state.count_approx() > 0
1113
1167
  }
1114
- `).malloyResultMatches(runtime, {x: true});
1168
+ `).malloyResultMatches(runtime, {x: booleanResult(true, databaseName)});
1115
1169
  });
1116
1170
  });
1117
1171
  describe('last_value', () => {
@@ -1262,7 +1316,10 @@ expressionModels.forEach((x, databaseName) => {
1262
1316
  describe('duckdb', () => {
1263
1317
  const duckdb = it.when(databaseName === 'duckdb');
1264
1318
  duckdb('to_timestamp', async () => {
1265
- await funcTest('to_timestamp(1725555835) = @2024-09-05 17:03:55', true);
1319
+ await funcTest(
1320
+ 'to_timestamp(1725555835) = @2024-09-05 17:03:55',
1321
+ booleanResult(true, databaseName)
1322
+ );
1266
1323
  });
1267
1324
  });
1268
1325
 
@@ -1271,7 +1328,7 @@ expressionModels.forEach((x, databaseName) => {
1271
1328
  trino('from_unixtime', async () => {
1272
1329
  await funcTest(
1273
1330
  'from_unixtime(1725555835) = @2024-09-05 17:03:55',
1274
- true
1331
+ booleanResult(true, databaseName)
1275
1332
  );
1276
1333
  });
1277
1334
  });
@@ -64,8 +64,10 @@ afterAll(async () => {
64
64
  runtimes.runtimeMap.forEach((runtime, databaseName) => {
65
65
  const q = runtime.getQuoter();
66
66
  // Issue #1824
67
- it(`not boolean field with null - ${databaseName}`, async () => {
68
- await expect(`
67
+ it.when(runtime.dialect.nativeBoolean)(
68
+ `not boolean field with null - ${databaseName}`,
69
+ async () => {
70
+ await expect(`
69
71
  run: ${databaseName}.sql("""
70
72
  SELECT
71
73
  CASE WHEN 1=1 THEN NULL ELSE false END as ${q`n`}
@@ -73,14 +75,17 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
73
75
  select:
74
76
  is_true is not n
75
77
  }
76
- `).malloyResultMatches(runtime, {is_true: true});
77
- });
78
+ `).malloyResultMatches(runtime, {
79
+ is_true: true,
80
+ });
81
+ }
82
+ );
78
83
 
79
84
  // Issue: #1284
80
85
  it(`parenthesize output field values - ${databaseName}`, async () => {
81
86
  await expect(`
82
87
  run: ${databaseName}.table('malloytest.aircraft') -> {
83
- group_by: r is 1
88
+ group_by: r is 1.0
84
89
 
85
90
  calculate:
86
91
  zero is 1 - rank()
@@ -413,11 +418,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
413
418
  });
414
419
  });
415
420
 
416
- it(`join full - ${databaseName}`, async () => {
417
- // a cross join produces a Many to Many result.
418
- // symmetric aggregate are needed on both sides of the join
419
- // Check the row count and that sums on each side work properly.
420
- await expect(`
421
+ it.when(runtime.dialect.supportsFullJoin)(
422
+ `join full - ${databaseName}`,
423
+ async () => {
424
+ // a cross join produces a Many to Many result.
425
+ // symmetric aggregate are needed on both sides of the join
426
+ // Check the row count and that sums on each side work properly.
427
+ await expect(`
421
428
  ${matrixModel}
422
429
  run: ac_states -> {
423
430
  extend: {
@@ -431,12 +438,13 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
431
438
 
432
439
  }
433
440
  `).malloyResultMatches(runtime, {
434
- ac_count: 49,
435
- ac_sum: 21336,
436
- am_count: 12,
437
- am_sum: 4139,
438
- });
439
- });
441
+ ac_count: 49,
442
+ ac_sum: 21336,
443
+ am_count: 12,
444
+ am_sum: 4139,
445
+ });
446
+ }
447
+ );
440
448
 
441
449
  it(`leafy count - ${databaseName}`, async () => {
442
450
  // in a joined table when the joined is leafiest
@@ -948,12 +956,13 @@ SELECT row_to_json(finalStage) as row FROM __stage0 AS finalStage`);
948
956
  ).malloyResultMatches(runtime, {a: 1});
949
957
  });
950
958
 
959
+ // weirdly '*' must be the first thing in the select list in MySQL
951
960
  it(`sql with turducken- ${databaseName}`, async () => {
952
961
  const turduckenQuery = `
953
962
  run: ${databaseName}.sql("""
954
963
  SELECT
955
- 'something' as SOMETHING,
956
964
  *
965
+ , 'something' as SOMETHING
957
966
  FROM %{
958
967
  ${databaseName}.table('malloytest.state_facts') -> {
959
968
  group_by: popular_name
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import {RuntimeList, allDatabases} from '../../runtimes';
26
- import {databasesFromEnvironmentOr} from '../../util';
26
+ import {booleanResult, databasesFromEnvironmentOr} from '../../util';
27
27
  import '../../util/db-jest-matchers';
28
28
 
29
29
  const runtimes = new RuntimeList(databasesFromEnvironmentOr(allDatabases));
@@ -45,7 +45,7 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
45
45
  aggregate: model_count is count()
46
46
  }
47
47
  `).malloyResultMatches(orderByModel, {
48
- big: false,
48
+ big: booleanResult(false, databaseName),
49
49
  model_count: 58451,
50
50
  });
51
51
  });
@@ -62,7 +62,7 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
62
62
  aggregate: model_count is model_count.sum()
63
63
  }
64
64
  `).malloyResultMatches(orderByModel, {
65
- big: false,
65
+ big: booleanResult(false, databaseName),
66
66
  model_count: 58500,
67
67
  });
68
68
  });
@@ -91,9 +91,9 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
91
91
  `reserved words are quoted in turtles - ${databaseName}`,
92
92
  async () => {
93
93
  await expect(`
94
- run: models->{
94
+ run: ${databaseName}.table('malloytest.state_facts')->{
95
95
  nest: withx is {
96
- group_by: select is UPPER(manufacturer)
96
+ group_by: select is upper(popular_name)
97
97
  aggregate: fetch is count()
98
98
  }
99
99
  } -> {
package/src/runtimes.ts CHANGED
@@ -38,6 +38,10 @@ import {PooledPostgresConnection} from '@malloydata/db-postgres';
38
38
  import {TrinoConnection, TrinoExecutor} from '@malloydata/db-trino';
39
39
  import {SnowflakeExecutor} from '@malloydata/db-snowflake/src/snowflake_executor';
40
40
  import {PrestoConnection} from '@malloydata/db-trino/src/trino_connection';
41
+ import {
42
+ MySQLConnection,
43
+ MySQLExecutor,
44
+ } from '@malloydata/db-mysql/src/mysql_connection';
41
45
  import {EventEmitter} from 'events';
42
46
 
43
47
  export class SnowflakeTestConnection extends SnowflakeConnection {
@@ -72,6 +76,23 @@ export class BigQueryTestConnection extends BigQueryConnection {
72
76
  }
73
77
  }
74
78
 
79
+ export class MySQLTestConnection extends MySQLConnection {
80
+ // we probably need a better way to do this.
81
+
82
+ public async runSQL(
83
+ sqlCommand: string,
84
+ options?: RunSQLOptions
85
+ ): Promise<MalloyQueryData> {
86
+ try {
87
+ return await super.runSQL(sqlCommand, options);
88
+ } catch (e) {
89
+ // eslint-disable-next-line no-console
90
+ console.log(`Error in SQL:\n ${sqlCommand}`);
91
+ throw e;
92
+ }
93
+ }
94
+ }
95
+
75
96
  export class PostgresTestConnection extends PooledPostgresConnection {
76
97
  // we probably need a better way to do this.
77
98
 
@@ -175,6 +196,13 @@ export function runtimeFor(dbName: string): SingleConnectionRuntime {
175
196
  TrinoExecutor.getConnectionOptionsFromEnv(dbName)
176
197
  );
177
198
  break;
199
+ case 'mysql':
200
+ connection = new MySQLConnection(
201
+ dbName,
202
+ MySQLExecutor.getConnectionOptionsFromEnv(),
203
+ {}
204
+ );
205
+ break;
178
206
  case 'presto':
179
207
  connection = new PrestoConnection(
180
208
  dbName,
@@ -208,6 +236,7 @@ export const allDatabases = [
208
236
  'duckdb_wasm',
209
237
  'snowflake',
210
238
  'trino',
239
+ 'mysql',
211
240
  ];
212
241
 
213
242
  type RuntimeDatabaseNames = (typeof allDatabases)[number];
package/src/util/index.ts CHANGED
@@ -198,3 +198,19 @@ export async function runQuery(runtime: Runtime, querySrc: string) {
198
198
 
199
199
  return result;
200
200
  }
201
+
202
+ export function booleanResult(value: boolean, dbName: string) {
203
+ if (dbName === 'mysql') {
204
+ return value ? 1 : 0;
205
+ } else {
206
+ return value;
207
+ }
208
+ }
209
+
210
+ export function booleanCode(value: boolean, dbName: string) {
211
+ if (dbName === 'mysql') {
212
+ return value ? '1' : '0';
213
+ } else {
214
+ return value ? 'true' : 'false';
215
+ }
216
+ }
package/tsconfig.json CHANGED
@@ -24,6 +24,9 @@
24
24
  {
25
25
  "path": "../packages/malloy-db-trino"
26
26
  },
27
+ {
28
+ "path": "../packages/malloy-db-mysql"
29
+ },
27
30
  {
28
31
  "path": "../packages/malloy-db-duckdb"
29
32
  },