@malloydata/malloy-tests 0.0.67 → 0.0.68-dev230808194809

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.
Files changed (81) hide show
  1. package/dist/databases/all/db_index.spec.d.ts +1 -0
  2. package/dist/databases/all/db_index.spec.js +36 -0
  3. package/dist/databases/all/db_index.spec.js.map +1 -0
  4. package/dist/databases/all/expr.spec.d.ts +0 -2
  5. package/dist/databases/all/expr.spec.js +2 -547
  6. package/dist/databases/all/expr.spec.js.map +1 -1
  7. package/dist/databases/all/functions.spec.d.ts +1 -2
  8. package/dist/databases/all/functions.spec.js +2 -751
  9. package/dist/databases/all/functions.spec.js.map +1 -1
  10. package/dist/databases/all/join.spec.d.ts +1 -2
  11. package/dist/databases/all/join.spec.js +2 -276
  12. package/dist/databases/all/join.spec.js.map +1 -1
  13. package/dist/databases/all/nomodel.spec.d.ts +1 -2
  14. package/dist/databases/all/nomodel.spec.js +2 -923
  15. package/dist/databases/all/nomodel.spec.js.map +1 -1
  16. package/dist/databases/all/orderby.spec.d.ts +1 -2
  17. package/dist/databases/all/orderby.spec.js +2 -191
  18. package/dist/databases/all/orderby.spec.js.map +1 -1
  19. package/dist/databases/all/problems.spec.d.ts +1 -2
  20. package/dist/databases/all/problems.spec.js +2 -80
  21. package/dist/databases/all/problems.spec.js.map +1 -1
  22. package/dist/databases/all/sql_expressions.spec.d.ts +1 -2
  23. package/dist/databases/all/sql_expressions.spec.js +2 -62
  24. package/dist/databases/all/sql_expressions.spec.js.map +1 -1
  25. package/dist/databases/all/time.spec.d.ts +1 -3
  26. package/dist/databases/all/time.spec.js +2 -615
  27. package/dist/databases/all/time.spec.js.map +1 -1
  28. package/dist/databases/{all/index.spec.d.ts → shared/db_index.d.ts} +1 -0
  29. package/dist/databases/{all/index.spec.js → shared/db_index.js} +2 -33
  30. package/dist/databases/shared/db_index.js.map +1 -0
  31. package/dist/databases/shared/expr.d.ts +3 -0
  32. package/dist/databases/shared/expr.js +551 -0
  33. package/dist/databases/shared/expr.js.map +1 -0
  34. package/dist/databases/shared/functions.d.ts +3 -0
  35. package/dist/databases/shared/functions.js +754 -0
  36. package/dist/databases/shared/functions.js.map +1 -0
  37. package/dist/databases/shared/join.d.ts +3 -0
  38. package/dist/databases/shared/join.js +302 -0
  39. package/dist/databases/shared/join.js.map +1 -0
  40. package/dist/databases/shared/nomodel.d.ts +3 -0
  41. package/dist/databases/shared/nomodel.js +950 -0
  42. package/dist/databases/shared/nomodel.js.map +1 -0
  43. package/dist/databases/shared/orderby.d.ts +3 -0
  44. package/dist/databases/shared/orderby.js +217 -0
  45. package/dist/databases/shared/orderby.js.map +1 -0
  46. package/dist/databases/shared/problems.d.ts +3 -0
  47. package/dist/databases/shared/problems.js +106 -0
  48. package/dist/databases/shared/problems.js.map +1 -0
  49. package/dist/databases/shared/sql_expressions.d.ts +3 -0
  50. package/dist/databases/shared/sql_expressions.js +88 -0
  51. package/dist/databases/shared/sql_expressions.js.map +1 -0
  52. package/dist/databases/shared/test_list.js +18 -18
  53. package/dist/databases/shared/test_list.js.map +1 -1
  54. package/dist/databases/shared/time.d.ts +3 -0
  55. package/dist/databases/shared/time.js +640 -0
  56. package/dist/databases/shared/time.js.map +1 -0
  57. package/dist/index.d.ts +9 -10
  58. package/dist/index.js +19 -21
  59. package/dist/index.js.map +1 -1
  60. package/package.json +6 -6
  61. package/src/databases/all/db_index.spec.ts +37 -0
  62. package/src/databases/all/expr.spec.ts +2 -670
  63. package/src/databases/all/functions.spec.ts +1 -1101
  64. package/src/databases/all/join.spec.ts +1 -315
  65. package/src/databases/all/nomodel.spec.ts +2 -1124
  66. package/src/databases/all/orderby.spec.ts +1 -234
  67. package/src/databases/all/problems.spec.ts +1 -87
  68. package/src/databases/all/sql_expressions.spec.ts +1 -71
  69. package/src/databases/all/time.spec.ts +1 -761
  70. package/src/databases/{all/index.spec.ts → shared/db_index.ts} +2 -13
  71. package/src/databases/shared/expr.ts +695 -0
  72. package/src/databases/shared/functions.ts +1126 -0
  73. package/src/databases/shared/join.ts +340 -0
  74. package/src/databases/shared/nomodel.ts +1150 -0
  75. package/src/databases/shared/orderby.ts +260 -0
  76. package/src/databases/shared/problems.ts +113 -0
  77. package/src/databases/shared/sql_expressions.ts +96 -0
  78. package/src/databases/shared/test_list.ts +9 -9
  79. package/src/databases/shared/time.ts +786 -0
  80. package/src/index.ts +10 -11
  81. package/dist/databases/all/index.spec.js.map +0 -1
@@ -0,0 +1,1150 @@
1
+ /* eslint-disable no-console */
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+
25
+ import {RuntimeList} from '../../runtimes';
26
+ import {testIf} from '../../util';
27
+ import '../../util/db-jest-matchers';
28
+
29
+ // No prebuilt shared model, each test is complete. Makes debugging easier.
30
+ export const noModelSharedTests = (
31
+ runtimes: RuntimeList,
32
+ splitFunction?: (column: string, splitChar: string) => string
33
+ ) => {
34
+ function rootDbPath(databaseName: string) {
35
+ return databaseName === 'bigquery' ? 'malloy-data.' : '';
36
+ }
37
+
38
+ // TODO: Figure out how to generalize this.
39
+ function getSplitFunction(db: string) {
40
+ return (
41
+ splitFunction ??
42
+ {
43
+ 'bigquery': (column: string, splitChar: string) =>
44
+ `split(${column}, '${splitChar}')`,
45
+ 'postgres': (column: string, splitChar: string) =>
46
+ `string_to_array(${column}, '${splitChar}')`,
47
+ 'duckdb': (column: string, splitChar: string) =>
48
+ `string_to_array(${column}, '${splitChar}')`,
49
+ 'duckdb_wasm': (column: string, splitChar: string) =>
50
+ `string_to_array(${column}, '${splitChar}')`,
51
+ }[db]
52
+ );
53
+ }
54
+
55
+ afterAll(async () => {
56
+ await runtimes.closeAll();
57
+ });
58
+
59
+ runtimes.runtimeMap.forEach((runtime, databaseName) => {
60
+ // Issue: #151
61
+ it(`unknown dialect - ${databaseName}`, async () => {
62
+ const result = await runtime
63
+ .loadQuery(
64
+ `
65
+ query: q is table('malloytest.aircraft')->{
66
+ where: state != null
67
+ group_by: state
68
+ }
69
+
70
+ source: r is from(->q){
71
+ query: foo is {
72
+ order_by: 1 desc
73
+ group_by: state
74
+ }
75
+ }
76
+
77
+ query: r->foo
78
+ `
79
+ )
80
+ .run();
81
+ // console.log(result.data.toObject());
82
+ expect(result.data.path(0, 'state').value).toBe('WY');
83
+ });
84
+
85
+ // Issue #149
86
+ it(`refine query from query - ${databaseName}`, async () => {
87
+ const result = await runtime
88
+ .loadQuery(
89
+ `
90
+ query: from(
91
+ table('malloytest.state_facts')->{group_by: state; order_by: 1 desc; limit: 1}
92
+ )
93
+ {
94
+ dimension: lower_state is lower(state)
95
+ }
96
+ -> {project: lower_state}
97
+ `
98
+ )
99
+ .run();
100
+ // console.log(result.data.toObject());
101
+ expect(result.data.path(0, 'lower_state').value).toBe('wy');
102
+ });
103
+
104
+ // issue #157
105
+ it(`source- not -found - ${databaseName}`, async () => {
106
+ // console.log(result.data.toObject());
107
+ let error;
108
+ try {
109
+ await runtime
110
+ .loadQuery(
111
+ `
112
+ source: foo is table('malloytest.state_facts'){primary_key: state}
113
+ query: foox->{aggregate: c is count()}
114
+ `
115
+ )
116
+ .run();
117
+ } catch (e) {
118
+ error = e;
119
+ }
120
+ expect(error.toString()).not.toContain('Unknown Dialect');
121
+ });
122
+
123
+ it(`join_many - ${databaseName}`, async () => {
124
+ const result = await runtime
125
+ .loadQuery(
126
+ `
127
+ source: a is table('malloytest.aircraft'){
128
+ measure: avg_year is floor(avg(year_built))
129
+ }
130
+ source: m is table('malloytest.aircraft_models'){
131
+ join_many: a on a.aircraft_model_code=aircraft_model_code
132
+ measure: avg_seats is floor(avg(seats))
133
+ }
134
+ query: m->{aggregate: avg_seats, a.avg_year}
135
+ `
136
+ )
137
+ .run();
138
+ expect(result.data.value[0]['avg_year']).toBe(1969);
139
+ expect(result.data.value[0]['avg_seats']).toBe(7);
140
+ });
141
+
142
+ it(`join_many condition no primary key - ${databaseName}`, async () => {
143
+ const result = await runtime
144
+ .loadQuery(
145
+ `
146
+ source: a is table('malloytest.airports'){}
147
+ source: b is table('malloytest.state_facts') {
148
+ join_many: a on state=a.state
149
+ }
150
+ query: b->{aggregate: c is airport_count.sum()}
151
+ `
152
+ )
153
+ .run();
154
+ expect(result.data.value[0]['c']).toBe(19701);
155
+ });
156
+
157
+ it(`join_many filter multiple values - ${databaseName}`, async () => {
158
+ const result = await runtime
159
+ .loadQuery(
160
+ `
161
+ source: a is table('malloytest.airports'){
162
+ where: state = 'NH' | 'CA'
163
+ }
164
+ source: b is table('malloytest.state_facts') {
165
+ join_many: a on state=a.state
166
+ }
167
+ query: b->{
168
+ aggregate: c is airport_count.sum()
169
+ group_by: a.state
170
+ }
171
+ `
172
+ )
173
+ .run();
174
+ expect(result.data.value[0]['c']).toBe(18605);
175
+ expect(result.data.value[0]['state']).toBeNull();
176
+ expect(result.data.value[1]['c']).toBe(984);
177
+ expect(result.data.value[1]['state']).toBe('CA');
178
+ expect(result.data.value[2]['c']).toBe(112);
179
+ expect(result.data.value[2]['state']).toBe('NH');
180
+ });
181
+
182
+ it(`join_one condition no primary key - ${databaseName}`, async () => {
183
+ const result = await runtime
184
+ .loadQuery(
185
+ `
186
+ source: a is table('malloytest.state_facts'){}
187
+ source: b is table('malloytest.airports') {
188
+ join_one: a on state=a.state
189
+ }
190
+ query: b->{aggregate: c is a.airport_count.sum()}
191
+
192
+ `
193
+ )
194
+ .run();
195
+ expect(result.data.value[0]['c']).toBe(19701);
196
+ });
197
+
198
+ it(`join_one filter multiple values - ${databaseName}`, async () => {
199
+ const result = await runtime
200
+ .loadQuery(
201
+ `
202
+ source: a is table('malloytest.state_facts'){
203
+ where: state = 'TX' | 'LA'
204
+ }
205
+ source: b is table('malloytest.airports') {
206
+ join_one: a on state=a.state
207
+ }
208
+ query: b->{
209
+ aggregate: c is a.airport_count.sum()
210
+ group_by: a.state
211
+ }
212
+ `
213
+ )
214
+ .run();
215
+ // https://github.com/malloydata/malloy/pull/501#discussion_r861022857
216
+ expect(result.data.value).toHaveLength(3);
217
+ expect(result.data.value).toContainEqual({c: 1845, state: 'TX'});
218
+ expect(result.data.value).toContainEqual({c: 500, state: 'LA'});
219
+ expect(result.data.value).toContainEqual({c: 0, state: null});
220
+ });
221
+
222
+ it(`join_many cross from - ${databaseName}`, async () => {
223
+ // a cross join produces a Many to Many result.
224
+ // symmetric aggregate are needed on both sides of the join
225
+ // Check the row count and that sums on each side work properly.
226
+ const result = await runtime
227
+ .loadQuery(
228
+ `
229
+ source: a is table('malloytest.state_facts')
230
+ source: f is a{
231
+ join_cross: a
232
+ }
233
+ query: f->{
234
+ aggregate:
235
+ row_count is count(distinct concat(state,a.state))
236
+ left_count is count()
237
+ right_count is a.count()
238
+ left_sum is airport_count.sum()
239
+ right_sum is a.airport_count.sum()
240
+ }
241
+ `
242
+ )
243
+ .run();
244
+ expect(result.data.value[0]['row_count']).toBe(51 * 51);
245
+ expect(result.data.value[0]['left_sum']).toBe(19701);
246
+ expect(result.data.value[0]['right_sum']).toBe(19701);
247
+ });
248
+
249
+ it(`join_one only - ${databaseName}`, async () => {
250
+ // a cross join produces a Many to Many result.
251
+ // symmetric aggregate are needed on both sides of the join
252
+ // Check the row count and that sums on each side work properly.
253
+ const result = await runtime
254
+ .loadQuery(
255
+ `
256
+ query: q is table('malloytest.state_facts')->{
257
+ aggregate: r is airport_count.sum()
258
+ }
259
+ source: f is table('malloytest.state_facts'){
260
+ join_one: a is from(->q)
261
+ }
262
+ query: f->{
263
+ aggregate:
264
+ row_count is count(distinct concat(state,a.r))
265
+ left_sum is airport_count.sum()
266
+ right_sum is a.r.sum()
267
+ sum_sum is sum(airport_count + a.r)
268
+ }
269
+ `
270
+ )
271
+ .run();
272
+ expect(result.data.value[0]['row_count']).toBe(51);
273
+ expect(result.data.value[0]['left_sum']).toBe(19701);
274
+ expect(result.data.value[0]['right_sum']).toBe(19701);
275
+ expect(result.data.value[0]['sum_sum']).toBe(19701 + 51 * 19701);
276
+ });
277
+
278
+ it(`join_many cross ON - ${databaseName}`, async () => {
279
+ // a cross join produces a Many to Many result.
280
+ // symmetric aggregate are needed on both sides of the join
281
+ // Check the row count and that sums on each side work properly.
282
+ const result = await runtime
283
+ .loadQuery(
284
+ `
285
+ source: a is table('malloytest.state_facts')
286
+ source: f is a{
287
+ join_cross: a on a.state = 'CA' | 'NY'
288
+ }
289
+ query: f->{
290
+ aggregate:
291
+ row_count is count(distinct concat(state,a.state))
292
+ left_sum is airport_count.sum()
293
+ right_sum is a.airport_count.sum()
294
+ }
295
+ `
296
+ )
297
+ .run();
298
+ expect(result.data.value[0]['row_count']).toBe(51 * 2);
299
+ expect(result.data.value[0]['left_sum']).toBe(19701);
300
+ expect(result.data.value[0]['right_sum']).toBe(1560);
301
+ });
302
+
303
+ it(`limit - provided - ${databaseName}`, async () => {
304
+ // a cross join produces a Many to Many result.
305
+ // symmetric aggregate are needed on both sides of the join
306
+ // Check the row count and that sums on each side work properly.
307
+ const result = await runtime
308
+ .loadQuery(
309
+ `
310
+ query: table('malloytest.state_facts') -> {
311
+ group_by: state
312
+ aggregate: c is count()
313
+ limit: 3
314
+ }
315
+ `
316
+ )
317
+ .run();
318
+ expect(result.resultExplore.limit).toBe(3);
319
+ });
320
+
321
+ testIf(runtime.supportsNesting)(
322
+ `number as null- ${databaseName}`,
323
+ async () => {
324
+ // a cross join produces a Many to Many result.
325
+ // symmetric aggregate are needed on both sides of the join
326
+ // Check the row count and that sums on each side work properly.
327
+ const result = await runtime
328
+ .loadQuery(
329
+ `
330
+ source: s is table('malloytest.state_facts') + {
331
+ }
332
+ query: s-> {
333
+ group_by: state
334
+ nest: ugly is {
335
+ group_by: popular_name
336
+ aggregate: foo is NULLIF(sum(airport_count)*0,0)+1
337
+ }
338
+ }
339
+ `
340
+ )
341
+ .run();
342
+ expect(result.data.path(0, 'ugly', 0, 'foo').value).toBe(null);
343
+ }
344
+ );
345
+
346
+ // average should only include non-null values in the denominator
347
+ it(`avg ignore null- ${databaseName}`, async () => {
348
+ const result = await runtime
349
+ .loadQuery(
350
+ `
351
+ sql: one is { select: """
352
+ SELECT 2 as a
353
+ UNION ALL SELECT 4
354
+ UNION ALL SELECT null
355
+ """}
356
+
357
+ query: from_sql(one) -> {
358
+ join_cross: b is from_sql(one)
359
+ aggregate:
360
+ avg_a is a.avg()
361
+ avg_b is b.a.avg()
362
+ }
363
+ `
364
+ )
365
+ .run();
366
+ expect(result.data.value[0]['avg_a']).toBe(3);
367
+ });
368
+
369
+ it(`limit - not provided - ${databaseName}`, async () => {
370
+ // a cross join produces a Many to Many result.
371
+ // symmetric aggregate are needed on both sides of the join
372
+ // Check the row count and that sums on each side work properly.
373
+ const result = await runtime
374
+ .loadQuery(
375
+ `
376
+ query: table('malloytest.state_facts') -> {
377
+ group_by: state
378
+ aggregate: c is count()
379
+ }
380
+ `
381
+ )
382
+ .run();
383
+ expect(result.resultExplore.limit).toBe(undefined);
384
+ });
385
+
386
+ it(`limit pipeline - provided - ${databaseName}`, async () => {
387
+ // a cross join produces a Many to Many result.
388
+ // symmetric aggregate are needed on both sides of the join
389
+ // Check the row count and that sums on each side work properly.
390
+ const result = await runtime
391
+ .loadQuery(
392
+ `
393
+ query: table('malloytest.state_facts') -> {
394
+ project: state
395
+ limit: 10
396
+ }
397
+ -> {
398
+ project: state
399
+ limit: 3
400
+ }
401
+ `
402
+ )
403
+ .run();
404
+ expect(result.resultExplore.limit).toBe(3);
405
+ });
406
+
407
+ it(`ungrouped top level - ${databaseName}`, async () => {
408
+ const result = await runtime
409
+ .loadQuery(
410
+ `
411
+ source: s is table('malloytest.state_facts') + {
412
+ measure: total_births is births.sum()
413
+ measure: births_per_100k is floor(total_births/ all(total_births) * 100000)
414
+ }
415
+
416
+ query:s-> {
417
+ group_by: state
418
+ aggregate: births_per_100k
419
+ }
420
+ `
421
+ )
422
+ .run();
423
+ // console.log(result.sql);
424
+ expect(result.data.path(0, 'births_per_100k').value).toBe(9742);
425
+ });
426
+
427
+ testIf(runtime.supportsNesting)(
428
+ `ungrouped top level with nested - ${databaseName}`,
429
+ async () => {
430
+ const result = await runtime
431
+ .loadQuery(
432
+ `
433
+ source: s is table('malloytest.state_facts') + {
434
+ measure: total_births is births.sum()
435
+ measure: births_per_100k is floor(total_births/ all(total_births) * 100000)
436
+ }
437
+
438
+ query:s-> {
439
+ group_by: state
440
+ aggregate: births_per_100k
441
+ nest: by_name is {
442
+ group_by: popular_name
443
+ aggregate: total_births
444
+ }
445
+ limit: 1000
446
+ }
447
+ `
448
+ )
449
+ .run();
450
+ // console.log(result.sql);
451
+ expect(result.data.path(0, 'births_per_100k').value).toBe(9742);
452
+ }
453
+ );
454
+
455
+ it(`ungrouped - eliminate rows - ${databaseName}`, async () => {
456
+ const result = await runtime
457
+ .loadQuery(
458
+ `
459
+ source: s is table('malloytest.state_facts') + {
460
+ measure: m is all(births.sum())
461
+ where: state='CA' | 'NY'
462
+ }
463
+
464
+ query:s-> {
465
+ group_by: state
466
+ aggregate: m
467
+ }
468
+ `
469
+ )
470
+ .run();
471
+ // console.log(result.sql);
472
+ expect(result.data.toObject().length).toBe(2);
473
+ });
474
+
475
+ testIf(runtime.supportsNesting)(
476
+ `ungrouped nested with no grouping above - ${databaseName}`,
477
+ async () => {
478
+ const result = await runtime
479
+ .loadQuery(
480
+ `
481
+ source: s is table('malloytest.state_facts') + {
482
+ measure: total_births is births.sum()
483
+ measure: births_per_100k is floor(total_births/ all(total_births) * 100000)
484
+ }
485
+
486
+ query: s-> {
487
+ aggregate: total_births
488
+ nest: by_name is {
489
+ group_by: popular_name
490
+ aggregate: births_per_100k
491
+ }
492
+ }
493
+
494
+ `
495
+ )
496
+ .run();
497
+ // console.log(result.sql);
498
+ expect(result.data.path(0, 'by_name', 0, 'births_per_100k').value).toBe(
499
+ 66703
500
+ );
501
+ }
502
+ );
503
+
504
+ testIf(runtime.supportsNesting)(
505
+ `ungrouped - partial grouping - ${databaseName}`,
506
+ async () => {
507
+ const result = await runtime
508
+ .loadQuery(
509
+ `
510
+ source: airports is table('malloytest.airports') {
511
+ measure: c is count()
512
+ }
513
+
514
+
515
+ query: airports -> {
516
+ where: state = 'TX' | 'NY'
517
+ group_by:
518
+ faa_region
519
+ state
520
+ aggregate:
521
+ c
522
+ all_ is all(c)
523
+ airport_count is c {? fac_type = 'AIRPORT'}
524
+ nest: fac_type is {
525
+ group_by: fac_type
526
+ aggregate:
527
+ c
528
+ all_ is all(c)
529
+ all_state_region is exclude(c,fac_type)
530
+ all_of_this_type is exclude(c, state, faa_region)
531
+ all_top is exclude(c, state, faa_region, fac_type)
532
+ }
533
+ }
534
+
535
+ `
536
+ )
537
+ .run();
538
+ // console.log(result.sql);
539
+ expect(result.data.path(0, 'fac_type', 0, 'all_').value).toBe(1845);
540
+ expect(
541
+ result.data.path(0, 'fac_type', 0, 'all_state_region').value
542
+ ).toBe(1845);
543
+ expect(
544
+ result.data.path(0, 'fac_type', 0, 'all_of_this_type').value
545
+ ).toBe(1782);
546
+ expect(result.data.path(0, 'fac_type', 0, 'all_top').value).toBe(2421);
547
+ }
548
+ );
549
+
550
+ testIf(runtime.supportsNesting)(
551
+ `ungrouped - all nested - ${databaseName}`,
552
+ async () => {
553
+ const result = await runtime
554
+ .loadQuery(
555
+ `
556
+ source: airports is table('malloytest.airports') {
557
+ measure: c is count()
558
+ }
559
+
560
+
561
+ query: airports -> {
562
+ where: state = 'TX' | 'NY'
563
+ group_by:
564
+ state
565
+ aggregate:
566
+ c
567
+ all_ is all(c)
568
+ airport_count is c {? fac_type = 'AIRPORT'}
569
+ nest: fac_type is {
570
+ group_by: fac_type, major
571
+ aggregate:
572
+ c
573
+ all_ is all(c)
574
+ all_major is all(c,major)
575
+ }
576
+ }
577
+
578
+
579
+ `
580
+ )
581
+ .run();
582
+ // console.log(result.sql);
583
+ expect(result.data.path(0, 'fac_type', 0, 'all_').value).toBe(1845);
584
+ expect(result.data.path(0, 'fac_type', 0, 'all_major').value).toBe(
585
+ 1819
586
+ );
587
+ }
588
+ );
589
+
590
+ testIf(runtime.supportsNesting)(
591
+ `ungrouped nested - ${databaseName}`,
592
+ async () => {
593
+ const result = await runtime
594
+ .loadQuery(
595
+ `
596
+ source: s is table('malloytest.state_facts') + {
597
+ measure: total_births is births.sum()
598
+ measure: births_per_100k is floor(total_births/ all(total_births) * 100000)
599
+ }
600
+
601
+ query:s -> {
602
+ group_by: popular_name
603
+ nest: by_state is {
604
+ group_by: state
605
+ aggregate: births_per_100k
606
+ }
607
+ }
608
+
609
+ `
610
+ )
611
+ .run();
612
+ // console.log(result.sql);
613
+ expect(
614
+ result.data.path(0, 'by_state', 0, 'births_per_100k').value
615
+ ).toBe(36593);
616
+ }
617
+ );
618
+
619
+ testIf(runtime.supportsNesting)(
620
+ `ungrouped nested expression - ${databaseName}`,
621
+ async () => {
622
+ const result = await runtime
623
+ .loadQuery(
624
+ `
625
+ source: s is table('malloytest.state_facts') + {
626
+ measure: total_births is births.sum()
627
+ measure: births_per_100k is floor(total_births/ all(total_births) * 100000)
628
+ }
629
+
630
+ query:s -> {
631
+ group_by: upper_name is upper(popular_name)
632
+ nest: by_state is {
633
+ group_by: state
634
+ aggregate: births_per_100k
635
+ }
636
+ }
637
+
638
+ `
639
+ )
640
+ .run();
641
+ // console.log(result.sql);
642
+ expect(
643
+ result.data.path(0, 'by_state', 0, 'births_per_100k').value
644
+ ).toBe(36593);
645
+ }
646
+ );
647
+
648
+ testIf(runtime.supportsNesting)(
649
+ `ungrouped nested group by float - ${databaseName}`,
650
+ async () => {
651
+ const result = await runtime
652
+ .loadQuery(
653
+ `
654
+ source: s is table('malloytest.state_facts') + {
655
+ measure: total_births is births.sum()
656
+ measure: ug is all(total_births)
657
+ }
658
+
659
+ query:s -> {
660
+ group_by: f is floor(airport_count/300.0)
661
+ nest: by_state is {
662
+ group_by: state
663
+ aggregate: ug
664
+ }
665
+ }
666
+
667
+ `
668
+ )
669
+ .run();
670
+ // console.log(result.sql);
671
+ // console.log(JSON.stringify(result.data.toObject(), null, 2));
672
+ expect(result.data.path(0, 'by_state', 0, 'ug').value).toBe(62742230);
673
+ }
674
+ );
675
+
676
+ it(`all with parameters - basic - ${databaseName}`, async () => {
677
+ const result = await runtime
678
+ .loadQuery(
679
+ `
680
+ source: s is table('malloytest.state_facts') + {
681
+ measure: total_births is births.sum()
682
+ }
683
+
684
+ query: s -> {
685
+ group_by: popular_name, state
686
+ aggregate:
687
+ total_births
688
+ all_births is all(total_births)
689
+ all_name is exclude(total_births, state)
690
+ }
691
+
692
+ `
693
+ )
694
+ .run();
695
+ // console.log(result.sql);
696
+ // console.log(JSON.stringify(result.data.toObject(), null, 2));
697
+ expect(result.data.path(0, 'all_births').value).toBe(295727065);
698
+ expect(result.data.path(0, 'all_name').value).toBe(197260594);
699
+ });
700
+
701
+ testIf(runtime.supportsNesting)(
702
+ `all with parameters - nest - ${databaseName}`,
703
+ async () => {
704
+ const result = await runtime
705
+ .loadQuery(
706
+ `
707
+ source: s is table('malloytest.state_facts') + {
708
+ measure: total_births is births.sum()
709
+ dimension: abc is floor(airport_count/300)
710
+ }
711
+
712
+ query: s -> {
713
+ group_by: abc
714
+ aggregate: total_births
715
+ nest: by_stuff is {
716
+ group_by: popular_name, state
717
+ aggregate:
718
+ total_births
719
+ all_births is all(total_births)
720
+ all_name is exclude(total_births, state)
721
+ }
722
+ }
723
+
724
+ `
725
+ )
726
+ .run();
727
+ // console.log(result.sql);
728
+ // console.log(JSON.stringify(result.data.toObject(), null, 2));
729
+ expect(result.data.path(0, 'by_stuff', 0, 'all_births').value).toBe(
730
+ 119809719
731
+ );
732
+ expect(result.data.path(0, 'by_stuff', 0, 'all_name').value).toBe(
733
+ 61091215
734
+ );
735
+ }
736
+ );
737
+
738
+ testIf(runtime.supportsNesting)(
739
+ `single value to udf - ${databaseName}`,
740
+ async () => {
741
+ const result = await runtime
742
+ .loadQuery(
743
+ `
744
+ source: f is table('malloytest.state_facts') {
745
+ query: fun is {
746
+ aggregate: t is count()
747
+ }
748
+ -> {
749
+ project: t1 is t+1
750
+ }
751
+ }
752
+ query: f-> {
753
+ nest: fun
754
+ }
755
+ `
756
+ )
757
+ .run();
758
+ // console.log(result.sql);
759
+ expect(result.data.path(0, 'fun', 0, 't1').value).toBe(52);
760
+ }
761
+ );
762
+
763
+ testIf(runtime.supportsNesting)(
764
+ `Multi value to udf - ${databaseName}`,
765
+ async () => {
766
+ const result = await runtime
767
+ .loadQuery(
768
+ `
769
+ source: f is table('malloytest.state_facts') {
770
+ query: fun is {
771
+ group_by: one is 1
772
+ aggregate: t is count()
773
+ }
774
+ -> {
775
+ project: t1 is t+1
776
+ }
777
+ }
778
+ query: f-> {
779
+ nest: fun
780
+ }
781
+ `
782
+ )
783
+ .run();
784
+ // console.log(result.sql);
785
+ // console.log(result.data.toObject());
786
+ expect(result.data.path(0, 'fun', 0, 't1').value).toBe(52);
787
+ }
788
+ );
789
+
790
+ testIf(runtime.supportsNesting)(
791
+ `Multi value to udf group by - ${databaseName}`,
792
+ async () => {
793
+ const result = await runtime
794
+ .loadQuery(
795
+ `
796
+ source: f is table('malloytest.state_facts') {
797
+ query: fun is {
798
+ group_by: one is 1
799
+ aggregate: t is count()
800
+ }
801
+ -> {
802
+ group_by: t1 is t+1
803
+ }
804
+ }
805
+ query: f-> {
806
+ nest: fun
807
+ }
808
+ `
809
+ )
810
+ .run();
811
+ // console.log(result.sql);
812
+ // console.log(result.data.toObject());
813
+ expect(result.data.path(0, 'fun', 0, 't1').value).toBe(52);
814
+ }
815
+ );
816
+
817
+ const sql1234 = `
818
+ sql: one is {select: """
819
+ SELECT 1 as a, 2 as b
820
+ UNION ALL SELECT 3, 4
821
+ """}`;
822
+
823
+ it(`sql_block - ${databaseName}`, async () => {
824
+ const result = await runtime
825
+ .loadQuery(
826
+ `
827
+ ${sql1234}
828
+ source: eone is from_sql(one) {}
829
+
830
+ query: eone -> { project: a }
831
+ `
832
+ )
833
+ .run();
834
+ expect(result.data.value[0]['a']).toBe(1);
835
+ });
836
+
837
+ it(`sql_block no explore- ${databaseName}`, async () => {
838
+ const result = await runtime
839
+ .loadQuery(
840
+ `
841
+ ${sql1234}
842
+ query: from_sql(one) -> { project: a }
843
+ `
844
+ )
845
+ .run();
846
+ expect(result.data.value[0]['a']).toBe(1);
847
+ });
848
+
849
+ it(`sql_block with turducken- ${databaseName}`, async () => {
850
+ if (databaseName !== 'postgres') {
851
+ const turduckenQuery = `
852
+ sql: state_as_sql is {
853
+ select: """
854
+ SELECT
855
+ ROW_NUMBER() OVER (ORDER BY state_count) as row_number,
856
+ *
857
+ FROM (%{
858
+ table('malloytest.state_facts')
859
+ -> {
860
+ group_by: popular_name
861
+ aggregate: state_count is count()
862
+ }
863
+ }%)
864
+ """
865
+ }
866
+ query: from_sql(state_as_sql) -> {
867
+ project: *; where: popular_name = 'Emma'
868
+ }`;
869
+ const result = await runtime.loadQuery(turduckenQuery).run();
870
+ expect(result.data.value[0]['state_count']).toBe(6);
871
+ }
872
+ });
873
+
874
+ // it(`sql_block version- ${databaseName}`, async () => {
875
+ // const result = await runtime
876
+ // .loadQuery(
877
+ // `
878
+ // sql: one is ||
879
+ // select version() as version
880
+ // ;;
881
+
882
+ // query: from_sql(one) -> { project: version }
883
+ // `
884
+ // )
885
+ // .run();
886
+ // expect(result.data.value[0].version).toBe("something");
887
+ // });
888
+
889
+ // local declarations
890
+ it(`local declarations external query - ${databaseName}`, async () => {
891
+ const result = await runtime
892
+ .loadQuery(
893
+ `
894
+ ${sql1234}
895
+ query: from_sql(one) -> {
896
+ declare: c is a + 1
897
+ project: c
898
+ }
899
+ `
900
+ )
901
+ .run();
902
+ expect(result.data.value[0]['c']).toBe(2);
903
+ });
904
+
905
+ it(`local declarations named query - ${databaseName}`, async () => {
906
+ const result = await runtime
907
+ .loadQuery(
908
+ `
909
+ ${sql1234}
910
+ source: foo is from_sql(one) + {
911
+ query: bar is {
912
+ declare: c is a + 1
913
+ project: c
914
+ }
915
+ }
916
+
917
+ query: foo-> bar
918
+ `
919
+ )
920
+ .run();
921
+ expect(result.data.value[0]['c']).toBe(2);
922
+ });
923
+
924
+ it(`local declarations refined named query - ${databaseName}`, async () => {
925
+ const result = await runtime
926
+ .loadQuery(
927
+ `
928
+ ${sql1234}
929
+ source: foo is from_sql(one) + {
930
+ query: bar is {
931
+ declare: c is a + 1
932
+ project: c
933
+ }
934
+
935
+ query: baz is bar + {
936
+ declare: d is c + 1
937
+ project: d
938
+ }
939
+ }
940
+
941
+ query: foo-> baz
942
+ `
943
+ )
944
+ .run();
945
+ expect(result.data.value[0]['d']).toBe(3);
946
+ });
947
+
948
+ it(`regexp match- ${databaseName}`, async () => {
949
+ const result = await runtime
950
+ .loadQuery(
951
+ `
952
+ sql: one is { select: """
953
+ SELECT 'hello mom' as a, 'cheese tastes good' as b
954
+ UNION ALL SELECT 'lloyd is a bozo', 'michael likes poetry'
955
+ """}
956
+
957
+ query: from_sql(one) -> {
958
+ aggregate: llo is count() {? a ~ r'llo'}
959
+ aggregate: m2 is count() {? a !~ r'bozo'}
960
+ }
961
+ `
962
+ )
963
+ .run();
964
+ expect(result.data.value[0]['llo']).toBe(2);
965
+ expect(result.data.value[0]['m2']).toBe(1);
966
+ });
967
+
968
+ it(`substitution precidence- ${databaseName}`, async () => {
969
+ const result = await runtime
970
+ .loadQuery(
971
+ `
972
+ sql: one is {select: """
973
+ SELECT 5 as a, 2 as b
974
+ UNION ALL SELECT 3, 4
975
+ """}
976
+
977
+ query: from_sql(one) -> {
978
+ declare: c is b + 4
979
+ project: x is a * c
980
+ }
981
+ `
982
+ )
983
+ .run();
984
+ expect(result.data.value[0]['x']).toBe(30);
985
+ });
986
+
987
+ it(`array unnest - ${databaseName}`, async () => {
988
+ const result = await runtime
989
+ .loadQuery(
990
+ `
991
+ sql: atitle is {select:"""
992
+ SELECT
993
+ city,
994
+ ${getSplitFunction(databaseName)!('city', ' ')} as words
995
+ FROM ${rootDbPath(databaseName)}malloytest.aircraft
996
+ """}
997
+
998
+ source: title is from_sql(atitle){}
999
+
1000
+ query: title -> {
1001
+ where: words.value != null
1002
+ group_by: words.value
1003
+ aggregate: c is count()
1004
+ }
1005
+ `
1006
+ )
1007
+ .run();
1008
+ expect(result.data.value[0]['c']).toBe(145);
1009
+ });
1010
+
1011
+ // make sure we can count the total number of elements when fanning out.
1012
+ it(`array unnest x 2 - ${databaseName}`, async () => {
1013
+ const result = await runtime
1014
+ .loadQuery(
1015
+ `
1016
+ sql: atitle is {select: """
1017
+ SELECT
1018
+ city,
1019
+ ${getSplitFunction(databaseName)!('city', ' ')} as words,
1020
+ ${getSplitFunction(databaseName)!('city', 'A')} as abreak
1021
+ FROM ${rootDbPath(databaseName)}malloytest.aircraft
1022
+ where city IS NOT null
1023
+ """}
1024
+
1025
+ source: title is from_sql(atitle){}
1026
+
1027
+ query: title -> {
1028
+ aggregate:
1029
+ b is count()
1030
+ c is words.count()
1031
+ a is abreak.count()
1032
+ }
1033
+ `
1034
+ )
1035
+ .run();
1036
+ expect(result.data.value[0]['b']).toBe(3552);
1037
+ expect(result.data.value[0]['c']).toBe(4586);
1038
+ expect(result.data.value[0]['a']).toBe(6601);
1039
+ });
1040
+
1041
+ testIf(runtime.supportsNesting)(`nest null - ${databaseName}`, async () => {
1042
+ const result = await runtime
1043
+ .loadQuery(
1044
+ `
1045
+ query: table('malloytest.airports') -> {
1046
+ where: faa_region = null
1047
+ group_by: faa_region
1048
+ aggregate: airport_count is count()
1049
+ nest: by_state is {
1050
+ where: state != null
1051
+ group_by: state
1052
+ aggregate: airport_count is count()
1053
+ }
1054
+ nest: by_state1 is {
1055
+ where: state != null
1056
+ group_by: state
1057
+ aggregate: airport_count is count()
1058
+ limit: 1
1059
+ }
1060
+ }
1061
+ `
1062
+ )
1063
+ .run();
1064
+
1065
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1066
+ const d: any = result.data.toObject();
1067
+ expect(d[0]['by_state']).not.toBe(null);
1068
+ expect(d[0]['by_state1']).not.toBe(null);
1069
+ });
1070
+
1071
+ testIf(runtime.supportsNesting)(
1072
+ `number as null- ${databaseName}`,
1073
+ async () => {
1074
+ const result = await runtime
1075
+ .loadQuery(
1076
+ `
1077
+ source: s is table('malloytest.state_facts') + {
1078
+ }
1079
+ query: s-> {
1080
+ group_by: state
1081
+ nest: ugly is {
1082
+ group_by: popular_name
1083
+ aggregate: foo is NULLIF(sum(airport_count)*0,0)+1
1084
+ }
1085
+ }
1086
+ `
1087
+ )
1088
+ .run();
1089
+ expect(result.data.path(0, 'ugly', 0, 'foo').value).toBe(null);
1090
+ }
1091
+ );
1092
+
1093
+ describe('quoting and strings', () => {
1094
+ const tick = "'";
1095
+ const back = '\\';
1096
+ test('backslash quote', async () => {
1097
+ const result = await runtime
1098
+ .loadQuery(
1099
+ `
1100
+ query: table('malloytest.state_facts') -> {
1101
+ project: tick is '${back}${tick}'
1102
+ }
1103
+ `
1104
+ )
1105
+ .run();
1106
+ expect(result.data.value[0]['tick']).toBe(tick);
1107
+ });
1108
+ test('backslash backslash', async () => {
1109
+ const result = await runtime
1110
+ .loadQuery(
1111
+ `
1112
+ query: table('malloytest.state_facts') -> {
1113
+ project: back is '${back}${back}'
1114
+ }
1115
+ `
1116
+ )
1117
+ .run();
1118
+ expect(result.data.value[0]['back']).toBe(back);
1119
+ });
1120
+
1121
+ testIf(runtime.supportsNesting)('spaces in names', async () => {
1122
+ const result = await runtime
1123
+ .loadQuery(
1124
+ `
1125
+ source: \`space race\` is table('malloytest.state_facts') {
1126
+ join_one: \`j space\` is table('malloytest.state_facts') on \`j space\`.state=state
1127
+ query: \`q u e r y\` is {
1128
+ group_by:
1129
+ \`P O P\` is popular_name
1130
+ \`J P O P\` is \`j space\`.popular_name
1131
+ aggregate: \`c o u n t\` is count()
1132
+ calculate:
1133
+ \`R O W\` is row_number()
1134
+ \`l a g\` is lag(\`P O P\`, 1)
1135
+ nest: \`by state\` is {
1136
+ group_by: \`J S\` is \`j space\`.state
1137
+ aggregate: \`c o u n t\` is count()
1138
+ }
1139
+ }
1140
+ }
1141
+
1142
+ query: \`space race\` -> \`q u e r y\`
1143
+ `
1144
+ )
1145
+ .run();
1146
+ expect(result.data.value[0]['c o u n t']).toBe(24);
1147
+ });
1148
+ });
1149
+ });
1150
+ };