@malloydata/malloy 0.0.240-dev250311202829 → 0.0.240-dev250311214430

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 (66) hide show
  1. package/dist/api/core.d.ts +1 -0
  2. package/dist/api/core.js +2 -1
  3. package/dist/lang/malloy-parse-info.d.ts +2 -10
  4. package/dist/lang/malloy-to-ast.d.ts +1 -0
  5. package/dist/lang/malloy-to-ast.js +9 -6
  6. package/dist/lang/malloy-to-stable-query.d.ts +76 -0
  7. package/dist/lang/malloy-to-stable-query.js +660 -0
  8. package/dist/lang/parse-log.d.ts +1 -0
  9. package/dist/lang/parse-malloy.d.ts +2 -9
  10. package/dist/lang/parse-malloy.js +7 -112
  11. package/dist/lang/parse-tree-walkers/model-annotation-walker.js +1 -1
  12. package/dist/lang/run-malloy-parser.d.ts +3 -0
  13. package/dist/lang/run-malloy-parser.js +53 -0
  14. package/dist/lang/syntax-errors/malloy-parser-error-listener.d.ts +4 -3
  15. package/dist/lang/syntax-errors/malloy-parser-error-listener.js +8 -4
  16. package/dist/lang/utils.d.ts +27 -1
  17. package/dist/lang/utils.js +78 -1
  18. package/package.json +3 -3
  19. package/dist/api/asynchronous.spec.d.ts +0 -1
  20. package/dist/api/asynchronous.spec.js +0 -240
  21. package/dist/api/sessioned.spec.d.ts +0 -1
  22. package/dist/api/sessioned.spec.js +0 -476
  23. package/dist/api/stateless.spec.d.ts +0 -1
  24. package/dist/api/stateless.spec.js +0 -400
  25. package/dist/lang/test/annotation.spec.d.ts +0 -14
  26. package/dist/lang/test/annotation.spec.js +0 -809
  27. package/dist/lang/test/composite-field-usage.spec.d.ts +0 -1
  28. package/dist/lang/test/composite-field-usage.spec.js +0 -261
  29. package/dist/lang/test/document-help-context-walker.spec.d.ts +0 -1
  30. package/dist/lang/test/document-help-context-walker.spec.js +0 -55
  31. package/dist/lang/test/document-symbol-walker.spec.d.ts +0 -1
  32. package/dist/lang/test/document-symbol-walker.spec.js +0 -209
  33. package/dist/lang/test/expressions.spec.d.ts +0 -1
  34. package/dist/lang/test/expressions.spec.js +0 -1181
  35. package/dist/lang/test/field-symbols.spec.d.ts +0 -1
  36. package/dist/lang/test/field-symbols.spec.js +0 -194
  37. package/dist/lang/test/find-table-path-walker.spec.d.ts +0 -1
  38. package/dist/lang/test/find-table-path-walker.spec.js +0 -50
  39. package/dist/lang/test/imports.spec.d.ts +0 -1
  40. package/dist/lang/test/imports.spec.js +0 -283
  41. package/dist/lang/test/lenses.spec.d.ts +0 -1
  42. package/dist/lang/test/lenses.spec.js +0 -384
  43. package/dist/lang/test/literals.spec.d.ts +0 -1
  44. package/dist/lang/test/literals.spec.js +0 -267
  45. package/dist/lang/test/locations.spec.d.ts +0 -1
  46. package/dist/lang/test/locations.spec.js +0 -679
  47. package/dist/lang/test/model-annotation-walker.spec.d.ts +0 -1
  48. package/dist/lang/test/model-annotation-walker.spec.js +0 -46
  49. package/dist/lang/test/model_serialization.spec.d.ts +0 -5
  50. package/dist/lang/test/model_serialization.spec.js +0 -116
  51. package/dist/lang/test/parameters.spec.d.ts +0 -1
  52. package/dist/lang/test/parameters.spec.js +0 -619
  53. package/dist/lang/test/parse.spec.d.ts +0 -1
  54. package/dist/lang/test/parse.spec.js +0 -893
  55. package/dist/lang/test/pretranslate.spec.d.ts +0 -1
  56. package/dist/lang/test/pretranslate.spec.js +0 -80
  57. package/dist/lang/test/query.spec.d.ts +0 -1
  58. package/dist/lang/test/query.spec.js +0 -1149
  59. package/dist/lang/test/source.spec.d.ts +0 -1
  60. package/dist/lang/test/source.spec.js +0 -570
  61. package/dist/lang/test/sql-block.spec.d.ts +0 -1
  62. package/dist/lang/test/sql-block.spec.js +0 -145
  63. package/dist/lang/test/syntax-errors.spec.d.ts +0 -1
  64. package/dist/lang/test/syntax-errors.spec.js +0 -142
  65. package/dist/model/utils.spec.d.ts +0 -1
  66. package/dist/model/utils.spec.js +0 -38
@@ -1,1149 +0,0 @@
1
- "use strict";
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
- Object.defineProperty(exports, "__esModule", { value: true });
25
- const test_translator_1 = require("./test-translator");
26
- require("./parse-expects");
27
- const model_1 = require("../../model");
28
- function getFirstQuerySegment(q) {
29
- const qSeg = q === null || q === void 0 ? void 0 : q.pipeline[0];
30
- if (qSeg && (0, model_1.isQuerySegment)(qSeg)) {
31
- return qSeg;
32
- }
33
- }
34
- function getFirstSegmentFields(q) {
35
- var _a;
36
- const qSeg = getFirstQuerySegment(q);
37
- return (_a = qSeg === null || qSeg === void 0 ? void 0 : qSeg.queryFields) !== null && _a !== void 0 ? _a : [];
38
- }
39
- function getFirstSegmentFieldNames(q) {
40
- const qf = getFirstSegmentFields(q);
41
- return qf.map(f => f.type === 'fieldref' && f.path.length === 1
42
- ? f.path[0]
43
- : `expected simple ref, got ${JSON.stringify(f)}`);
44
- }
45
- describe('query:', () => {
46
- describe('basic query syntax', () => {
47
- test('run:anonymous query', () => expect('run: a -> { group_by: astr }').toTranslate());
48
- test('query:anonymous query m4 warning', () => {
49
- expect((0, test_translator_1.markSource) `##! m4warnings=warn
50
- query: ${'a -> { group_by: astr }'}`).toLog((0, test_translator_1.warningMessage)('Anonymous `query:` statements are deprecated, use `run:` instead'));
51
- });
52
- test('named query:', () => expect('query: aq is a -> { group_by: astr }').toTranslate());
53
- test('run query ref', () => expect(`
54
- query: foo is a -> { group_by: astr }
55
- run: foo
56
- `).toTranslate());
57
- test('query from query', () => {
58
- expect(`
59
- query: q1 is ab->{ group_by: astr limit: 10 }
60
- query: q2 is q1
61
- `).toTranslate();
62
- });
63
- test('query with refinements from query', () => {
64
- expect(`
65
- query: q1 is ab->{ group_by: astr limit: 10 }
66
- query: q2 is q1 + { aggregate: acount }
67
- `).toTranslate();
68
- });
69
- test('chained query operations', () => {
70
- expect(`
71
- run: ab
72
- -> { group_by: astr; aggregate: acount }
73
- -> { top: 5; where: astr ~ 'a%' group_by: astr }
74
- `).toTranslate();
75
- });
76
- test('query output refined into another query', () => {
77
- expect('run: ab -> {group_by: astr} extend { dimension: bigstr is upper(astr) } -> { group_by: bigstr }').toTranslate();
78
- });
79
- test('query with shortcut filtered turtle', () => {
80
- expect(`##! -m4warnings
81
- query: allA is ab -> aturtle + {where: astr ~ 'a%' }`).toTranslate();
82
- });
83
- test('query with filtered turtle', () => {
84
- expect("query: allA is ab -> aturtle + { where: astr ~ 'a%' }").toTranslate();
85
- });
86
- test('nest: in group_by:', () => {
87
- expect(`
88
- run: ab -> {
89
- group_by: astr;
90
- nest: nested_count is {
91
- aggregate: acount
92
- }
93
- }
94
- `).toTranslate();
95
- });
96
- test('reduce pipe project', () => {
97
- expect(`
98
- run: a -> { aggregate: f is count() } -> { select: f2 is f + 1 }
99
- `).toTranslate();
100
- });
101
- test('refine and extend query', () => {
102
- expect(`
103
- query: a_by_str is a -> { group_by: astr }
104
- run: a_by_str + { aggregate: str_count is count() }
105
- `).toTranslate();
106
- });
107
- test('query refinement preserves original', () => {
108
- const x = new test_translator_1.TestTranslator(`
109
- query: q is a -> { aggregate: acount is count() }
110
- query: nq is q + { group_by: astr }
111
- `);
112
- expect(x).toTranslate();
113
- const q = getFirstSegmentFields(x.getQuery('q'));
114
- expect(q).toBeDefined();
115
- if (q) {
116
- expect(q.length).toBe(1);
117
- }
118
- });
119
- test('query composition preserves original', () => {
120
- const x = new test_translator_1.TestTranslator(`
121
- query: q is ab -> { aggregate: acount }
122
- query: nq is q -> { select: * }
123
- `);
124
- expect(x).toTranslate();
125
- const q = x.getQuery('q');
126
- expect(q).toBeDefined();
127
- if (q) {
128
- expect(q.pipeline.length).toBe(1);
129
- }
130
- });
131
- test('all ungroup with args', () => {
132
- expect(`
133
- run: a -> {
134
- group_by: astr
135
- nest: by_int is {
136
- group_by: ai
137
- aggregate: bi_count is all(count(), ai)
138
- }
139
- }
140
- `).toTranslate();
141
- });
142
- test('all ungroup checks args', () => {
143
- expect(`
144
- run: a -> {
145
- group_by: astr
146
- nest: by_int is {
147
- group_by: ai
148
- aggregate: bi_count is all(count(), afloat)
149
- }
150
- }`).toLog((0, test_translator_1.errorMessage)("all() 'afloat' is missing from query output"));
151
- });
152
- test('exclude ungroup with args', () => {
153
- expect(`
154
- run: a -> {
155
- group_by: aa is 'a'
156
- nest: by_b is {
157
- group_by: bb is 'b'
158
- nest: by_c is {
159
- group_by: cc is 'c'
160
- aggregate: bb_count is exclude(count(), aa, cc)
161
- }
162
- }
163
- }
164
- `).toTranslate();
165
- });
166
- test('exclude ungroup checks args', () => {
167
- expect(`
168
- run: a -> {
169
- group_by: aa is 'a'
170
- nest: by_b is {
171
- group_by: bb is 'b'
172
- nest: by_c is {
173
- group_by: cc is 'c'
174
- aggregate: bb_count is exclude(count(), aaa, cc)
175
- }
176
- }
177
- }
178
- `).toLog((0, test_translator_1.errorMessage)("exclude() 'aaa' is missing from query output"));
179
- });
180
- test('exclude problem revealed by production models', () => {
181
- expect(`
182
- source: carriers is _db_.table('malloytest.carriers') extend {
183
- primary_key: code
184
- }
185
- source: flights is _db_.table('malloytest.flights') extend {
186
- primary_key: id2
187
- join_one: carriers with carrier
188
-
189
- view: carrier_overview is {
190
- group_by: carrier_name is carriers.nickname
191
- nest: top_destinations is {
192
- group_by: destination
193
- aggregate:
194
- flights_to_dest is exclude(count(), carrier_name)*100
195
- }
196
- }
197
- }
198
- `).toTranslate();
199
- });
200
- test('exclude output checking survives refinement', () => {
201
- // This was https://github.com/malloydata/malloy/issues/1474
202
- const nestExclude = (0, test_translator_1.model) `
203
- source: flights is a extend {
204
- dimension: carrier is astr, destination is astr
205
- measure: flight_count is count()
206
- view: by_dest is {
207
- group_by: destination
208
- aggregate: flight_count
209
- }
210
- }
211
- run: flights -> {
212
- group_by: carrier
213
- nest: broken is by_dest + {
214
- top: 5
215
- aggregate: flights_to_dest_all_carriers is exclude(flight_count, carrier)
216
- }
217
- }`;
218
- expect(nestExclude).toTranslate();
219
- });
220
- });
221
- describe('query operation typechecking', () => {
222
- describe('field declarations', () => {
223
- test('cannot use aggregate in group_by', () => {
224
- expect('run: a -> { group_by: s is count()}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
225
- });
226
- test('cannot use ungrouped_aggregate in group_by', () => {
227
- expect('run: a -> { group_by: s is all(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
228
- });
229
- test('cannot use analytic in group_by', () => {
230
- expect('run: a -> { group_by: s is row_number()}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a group_by operation, did you mean to use a calculate operation instead?'));
231
- });
232
- test('cannot use aggregate in dimension', () => {
233
- expect('source: a1 is a extend { dimension: s is count()}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'));
234
- });
235
- test('cannot use ungrouped_aggregate in dimension', () => {
236
- expect('source: a1 is a extend { dimension: s is all(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'));
237
- });
238
- test('cannot use analytic in dimension', () => {
239
- expect('source: a1 is a extend { dimension: s is row_number()}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a dimension declaration'));
240
- });
241
- test('cannot use scalar in measure', () => {
242
- expect('source: a1 is a extend { measure: s is 1}').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?'));
243
- });
244
- test('cannot use analytic in measure', () => {
245
- expect('source: a1 is a extend { measure: s is lag(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a measure declaration'));
246
- });
247
- test('cannot use scalar in aggregate', () => {
248
- expect('run: a -> { aggregate: s is 1}').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?'));
249
- });
250
- test('cannot use analytic in aggregate', () => {
251
- expect('run: a -> { aggregate: s is lag(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?'));
252
- });
253
- test('cannot use scalar in calculate', () => {
254
- expect('run: a -> { group_by: a is 1; calculate: s is 1 }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
255
- });
256
- test('cannot use aggregate in calculate', () => {
257
- expect('run: a -> { group_by: a is 1; calculate: s is count() }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
258
- });
259
- test('cannot use aggregate in project', () => {
260
- expect('run: a -> { select: s is count() }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a select operation, did you mean to use an aggregate operation instead?'));
261
- });
262
- test('cannot use analytic in project', () => {
263
- expect('run: a -> { select: s is row_number() }').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a select operation, did you mean to use a calculate operation instead?'));
264
- });
265
- test('cannot use analytic in extended source', () => {
266
- expect(`##! -m4warnings
267
- run: a -> { group_by: a is 1; declare: s is row_number() }`).toLog((0, test_translator_1.errorMessage)('Analytic expressions can not be used in a declare block'));
268
- });
269
- test('cannot use aggregate in index', () => {
270
- expect('run: a extend { measure: acount is count() } -> { index: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in an index operation'));
271
- });
272
- test('can use aggregate in except', () => {
273
- expect(`
274
- source: b1 is a extend { measure: acount is count() }
275
- source: c1 is b1 extend { except: acount }
276
- `).toTranslate();
277
- });
278
- });
279
- describe('field references', () => {
280
- test('cannot use aggregate in group_by', () => {
281
- expect('run: a -> { extend: {measure: acount is count()} group_by: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
282
- });
283
- test('cannot use query in group_by', () => {
284
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { group_by: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a group_by operation, did you mean to use a nest operation instead?'));
285
- });
286
- test('cannot use scalar in aggregate', () => {
287
- expect('run: a -> { extend: {dimension: aconst is 1} aggregate: aconst }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?'));
288
- });
289
- test('cannot use scalar in calculate', () => {
290
- expect('run: a -> { extend: {dimension: aconst is 1} group_by: x is 1; calculate: aconst }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
291
- });
292
- test('cannot use aggregate in calculate', () => {
293
- expect('run: a -> { extend: {measure: acount is count()} group_by: x is 1; calculate: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
294
- });
295
- test('cannot use query in project', () => {
296
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { select: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a select operation, did you mean to use a nest operation instead?'));
297
- });
298
- test('cannot use query in index', () => {
299
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { index: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in an index operation'));
300
- });
301
- test('cannot use query in calculate', () => {
302
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { group_by: x is 1; calculate: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a calculate operation, did you mean to use a nest operation instead?'));
303
- });
304
- test('cannot use query in aggregate', () => {
305
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { aggregate: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in an aggregate operation, did you mean to use a nest operation instead?'));
306
- });
307
- test('cannot use aggregate in calculate, preserved over refinement', () => {
308
- expect(`query: a1 is a -> {
309
- aggregate: c is count()
310
- }
311
- run: a1 + {
312
- calculate: b is c
313
- }`).toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
314
- });
315
- test('cannot use scalar in calculate, preserved over refinement', () => {
316
- expect(`query: a1 is a -> {
317
- group_by: c is 1
318
- }
319
- run: a1 + {
320
- calculate: b is c
321
- }`).toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
322
- });
323
- test('cannot use analytic in group_by, preserved over refinement', () => {
324
- expect(`query: a1 is a -> {
325
- group_by: c is 1
326
- calculate: c2 is lag(c)
327
- }
328
- run: a1 + {
329
- group_by: b is c2
330
- }`).toLog(
331
- // c2 is not defined because group_by doesn't know to look in the output space
332
- (0, test_translator_1.errorMessage)("'c2' is not defined"));
333
- });
334
- test('cannot use analytic in order_by, preserved over refinement', () => {
335
- expect(`query: a1 is a -> {
336
- group_by: c is 1
337
- calculate: c2 is lag(c)
338
- }
339
- run: a1 + {
340
- order_by: c2
341
- }`).toLog((0, test_translator_1.errorMessage)('Illegal order by of analytic field c2'));
342
- });
343
- test('cannot ungroup an ungrouped', () => {
344
- expect(`query: a1 is a -> {
345
- group_by: c is 1
346
- aggregate: c2 is all(all(sum(ai)))
347
- }`).toLog((0, test_translator_1.errorMessage)('all() expression must not already be ungrouped'));
348
- });
349
- test('cannot aggregate an ungrouped', () => {
350
- expect(`query: a1 is a -> {
351
- group_by: c is 1
352
- aggregate: c2 is sum(all(sum(ai)))
353
- }`).toLog((0, test_translator_1.errorMessage)('Aggregate expression cannot be aggregate'));
354
- });
355
- test('cannot aggregate an aggregate', () => {
356
- expect(`query: a1 is a -> {
357
- group_by: c is 1
358
- aggregate: c2 is sum(sum(ai))
359
- }`).toLog((0, test_translator_1.errorMessage)('Aggregate expression cannot be aggregate'));
360
- });
361
- test('can use field def in group_by, preserved over refinement', () => {
362
- expect(`query: a1 is a -> {
363
- group_by: c is 1
364
- }
365
- run: a1 + {
366
- order_by: c
367
- }`).toTranslate();
368
- });
369
- test('can use field ref in group_by, preserved over refinement', () => {
370
- expect(`query: a1 is a -> {
371
- group_by: c is astr
372
- }
373
- run: a1 + {
374
- order_by: c
375
- }`).toTranslate();
376
- });
377
- });
378
- });
379
- describe('function typechecking', () => {
380
- test('use function correctly', () => {
381
- expect(`run: a -> {
382
- group_by: s is concat('a', 'b')
383
- }`).toTranslate();
384
- });
385
- test('function incorrect case', () => {
386
- expect(`run: a -> {
387
- group_by: s is CONCAT('a', 'b')
388
- }`).toLog((0, test_translator_1.warningMessage)("Case insensitivity for function names is deprecated, use 'concat' instead"));
389
- });
390
- test('function no matching overload', () => {
391
- expect(`run: a -> {
392
- group_by: s is floor('a', 'b')
393
- }`).toLog((0, test_translator_1.errorMessage)('No matching overload for function floor(string, string)'));
394
- });
395
- test('unknown function', () => {
396
- expect(`run: a -> {
397
- group_by: s is asdfasdf()
398
- }`).toLog((0, test_translator_1.errorMessage)("Unknown function 'asdfasdf'. Use 'asdfasdf!(...)' to call a SQL function directly."));
399
- });
400
- test('can select different overload', () => {
401
- expect('run: a -> { group_by: s is concat() }').toTranslate();
402
- });
403
- test('can pass different expression types', () => {
404
- expect(`run: a -> {
405
- group_by: f1 is sqrt(1)
406
- aggregate: f2 is sqrt(count())
407
- aggregate: f3 is sqrt(all(count()))
408
- calculate: f4 is sqrt(lag(f1))
409
- calculate: f5 is sqrt(lag(count()))
410
- }`).toTranslate();
411
- });
412
- test('function return type correct', () => {
413
- expect(`run: a -> {
414
- group_by: s is floor(1.2) + 1
415
- }`).toTranslate();
416
- });
417
- test('function return type incorrect', () => {
418
- expect(`run: a -> {
419
- group_by: s is floor(1.2) + 'a'
420
- }`).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
421
- });
422
- test('can use output value in calculate', () => {
423
- expect(`run: a -> {
424
- group_by: x is 1
425
- calculate: s is lag(x)
426
- }`).toTranslate();
427
- });
428
- test('cannot use output value in group_by', () => {
429
- expect(`run: a -> {
430
- group_by: x is 1
431
- group_by: y is x
432
- }`).toLog((0, test_translator_1.errorMessage)("'x' is not defined"));
433
- });
434
- test('lag can check that other args are constant', () => {
435
- expect(`run: a -> {
436
- group_by: x is 1
437
- calculate: s is lag(x, 1, x)
438
- }`).toLog(
439
- // TODO improve this error message
440
- (0, test_translator_1.errorMessage)("Parameter 3 ('default') of lag must be literal or constant, but received output"));
441
- });
442
- test('lag can check that other args are literal', () => {
443
- expect(`run: a -> {
444
- group_by: x is 1
445
- calculate: s is lag(x, 1 + 1)
446
- }`).toLog(
447
- // TODO improve this error message
448
- (0, test_translator_1.errorMessage)("Parameter 2 ('offset') of lag must be literal, but received constant"));
449
- });
450
- test('lag can check that other args are nonnull', () => {
451
- expect(`run: a -> {
452
- group_by: x is 1
453
- calculate: s is lag(x, null)
454
- }`).toLog((0, test_translator_1.errorMessage)("Parameter 2 ('offset') of lag must not be a literal null"));
455
- });
456
- test('lag can use constant values for other args', () => {
457
- expect(`run: a -> {
458
- group_by: x is 1
459
- calculate: s is lag(x, 2)
460
- }`).toTranslate();
461
- });
462
- test('cannot name top level objects same as functions', () => {
463
- expect((0, test_translator_1.markSource) `query: ${'concat is a -> { group_by: x is 1 }'}`).toLog(
464
- // TODO improve this error message
465
- (0, test_translator_1.errorMessage)("'concat' is already defined, cannot redefine"));
466
- });
467
- test('`now` is considered constant`', () => {
468
- expect(`run: a -> {
469
- group_by: n is now
470
- calculate: l is lag(n, 1, now)
471
- }`).toTranslate();
472
- });
473
- // TODO it might be nice to reference a field which is a constant, and be able to
474
- // use that as a constant param. Same with a literal.
475
- test('cannot use a field which is a constant in a constant param', () => {
476
- expect(`run: a -> {
477
- group_by: ai, pi is pi()
478
- calculate: l is lag(ai, 1, pi)
479
- }`).toLog((0, test_translator_1.errorMessage)("Parameter 3 ('default') of lag must be literal or constant, but received output"));
480
- });
481
- test('cannot use struct in function arg', () => {
482
- expect(`run: ab -> {
483
- group_by: b.astr
484
- calculate: foo is lag(b)
485
- }`).toLog((0, test_translator_1.errorMessage)('No matching overload for function lag(table)'));
486
- });
487
- // TODO this doesn't work today, we're not rigorous enough with integer
488
- // subtypes. But we should probably make this typecheck properly.
489
- test.skip('cannot use float in round precision', () => {
490
- expect(`run: a -> {
491
- group_by: x is round(1.5, 1.6)
492
- }`).toLog(
493
- // TODO improve this error message
494
- (0, test_translator_1.errorMessage)("Parameter 2 ('precision') for round must be integer, received float"));
495
- });
496
- test('cannot use stddev with no arguments', () => {
497
- expect(`run: a -> {
498
- aggregate: x is stddev()
499
- }`).toLog((0, test_translator_1.errorMessage)('No matching overload for function stddev()'));
500
- });
501
- test('can use stddev with postfix syntax', () => {
502
- expect(`run: a -> {
503
- extend: { dimension: y is 1 }
504
- aggregate: x is y.stddev()
505
- }`).toTranslate();
506
- });
507
- test('can use stddev with postfix syntax on join', () => {
508
- expect(`run: a -> {
509
- extend: { join_one: b with astr }
510
- aggregate: x is b.ai.stddev()
511
- }`).toTranslate();
512
- });
513
- test('can use calculate with a measure', () => {
514
- expect(`run: a extend { measure: c is count() } -> {
515
- group_by: y is 1
516
- calculate: x is lag(c)
517
- }`).toTranslate();
518
- });
519
- test('cannot use calculate with input fields', () => {
520
- expect(`run: a -> {
521
- group_by: y is 1
522
- calculate: x is lag(ai)
523
- }`).toLog(
524
- // TODO improve this error message:
525
- // Parameter 1 ('value') of 'lag' must be a constant, an aggregate, or an expression using
526
- // only fields that appear in the query output. Received an expression which uses a field
527
- // that is not in the query output.
528
- (0, test_translator_1.errorMessage)("Parameter 1 ('value') of lag must be literal, constant or output, but received input"));
529
- });
530
- test('can use calculate with aggregate field which is not in query', () => {
531
- expect(`run: a extend { measure: acount is count() } -> {
532
- group_by: astr
533
- calculate: pc is lag(acount)
534
- }`).toTranslate();
535
- });
536
- test('cannot use agregate as argument to agg function', () => {
537
- expect(`run: a -> {
538
- aggregate: x is stddev(count())
539
- }`).toLog((0, test_translator_1.errorMessage)("Parameter 1 ('value') of stddev must be scalar, but received aggregate"));
540
- });
541
- test('cannot use calculate with no other fields', () => {
542
- expect(`run: a -> {
543
- calculate: x is row_number()
544
- }`).toLog((0, test_translator_1.error)('ambiguous-view-type', {}));
545
- });
546
- // TODO someday make it so we can order by an analytic function
547
- test('today: cannot order by analytic function', () => {
548
- expect(`run: a -> {
549
- group_by: astr
550
- calculate: row_num is row_number()
551
- order_by: row_num desc
552
- }`).toLog((0, test_translator_1.errorMessage)('Illegal order by of analytic field row_num'));
553
- });
554
- test('cannot use analytic in calculate -- and preserved over refinement', () => {
555
- expect(`query: a1 is a -> {
556
- group_by: astr
557
- calculate: p is lag(astr)
558
- }
559
- run: a1 + {
560
- calculate: p1 is lag(p)
561
- }`).toLog((0, test_translator_1.errorMessage)("Parameter 1 ('value') of lag must be scalar or aggregate, but received scalar_analytic"));
562
- });
563
- test('cannot use aggregate analytic in project', () => {
564
- expect(`run: a -> {
565
- select: astr
566
- calculate: p is lag(count())
567
- }`).toLog((0, test_translator_1.errorMessage)('Cannot add aggregate analyics to select'));
568
- });
569
- test('reference field in join', () => {
570
- expect(`run: a -> {
571
- extend: { join_one: b with astr }
572
- group_by: b.ai
573
- }`).toTranslate();
574
- });
575
- test('reference field in double nested join inside nest', () => {
576
- expect(`
577
- source: a_2 is a extend { join_one: b is b extend {
578
- join_one: c is b on 1 = c.ai
579
- } with astr }
580
-
581
- run: a_2 -> {
582
- nest: x is {
583
- group_by: ai is b.c.ai
584
- }
585
- }
586
- `).toTranslate();
587
- });
588
- test.skip('reference join as field', () => {
589
- expect(`run: a -> {
590
- extend: { join_one: b with astr }
591
- group_by: b
592
- }`).toLog((0, test_translator_1.errorMessage)('foo'));
593
- });
594
- test('can reference select: inline join.* field in calculate', () => {
595
- expect(`run: a -> {
596
- extend: { join_one: b with astr }
597
- select: b.*
598
- calculate: s is lag(ai)
599
- }`).toTranslate();
600
- });
601
- test('can reference select: join.* field in calculate', () => {
602
- expect(`run: a extend { join_one: b with astr } -> {
603
- select: b.*
604
- calculate: s is lag(ai)
605
- }`).toTranslate();
606
- });
607
- test('can reference implied output entries in calculate', () => {
608
- expect(`source: aaa is a extend { dimension: big_i is ai+10 }
609
- run: a extend { join_one: aaa on astr=aaa.astr } -> {
610
- group_by: aaa.big_i
611
- aggregate: s is aaa.big_i.sum()
612
- calculate: a is lag(big_i)
613
- }`).toTranslate();
614
- });
615
- describe('dialect functions', () => {
616
- test('can use function enabled in this dialect (standardsql)', () => {
617
- expect(`run: a -> {
618
- group_by: d is date_from_unix_date(1000)
619
- }`).toTranslate();
620
- });
621
- test('cannot use function enabled in a different dialect (duckdb)', () => {
622
- expect(`run: a -> {
623
- group_by: ts is to_timestamp(1000)
624
- }`).toLog((0, test_translator_1.errorMessage)(/Unknown function/));
625
- });
626
- });
627
- });
628
- describe('qops', () => {
629
- test('group by single', () => {
630
- expect('run: a->{ group_by: astr }').toTranslate();
631
- });
632
- test("group_by x is x'", () => {
633
- expect('run: a->{ group_by: ai is ai/2 }').toTranslate();
634
- });
635
- test('group by multiple', () => {
636
- expect('run: a->{ group_by: astr,ai }').toTranslate();
637
- });
638
- test('aggregate single', () => {
639
- expect('run: a->{ aggregate: num is count() }').toTranslate();
640
- });
641
- test('calculate in reduce', () => {
642
- expect('run: a->{ group_by: astr, ai calculate: num is lag(ai) }').toTranslate();
643
- });
644
- test('calculate in project', () => {
645
- expect('run: a->{ select: astr, ai calculate: num is lag(ai) }').toTranslate();
646
- });
647
- test('aggregate reference', () => {
648
- const doc = (0, test_translator_1.model) `run: a->{ aggregate: ai.sum() }`;
649
- expect(doc).toTranslate();
650
- const q = getFirstSegmentFields(doc.translator.getQuery(0));
651
- expect(q).toBeDefined();
652
- expect(q[0]).toMatchObject({
653
- name: 'ai',
654
- type: 'number',
655
- expressionType: 'aggregate',
656
- });
657
- });
658
- test('timeunit reference', () => {
659
- const doc = (0, test_translator_1.model) `run: a->{ group_by: ats.day }`;
660
- expect(doc).toTranslate();
661
- const q = getFirstSegmentFields(doc.translator.getQuery(0));
662
- expect(q).toBeDefined();
663
- expect(q[0]).toMatchObject({ name: 'ats', type: 'timestamp' });
664
- });
665
- test('aggregate multiple', () => {
666
- expect(`
667
- run: a->{
668
- aggregate: num is count(), total is sum(ai)
669
- }
670
- `).toTranslate();
671
- });
672
- test('project ref', () => {
673
- expect('run:ab->{ select: b.astr }').toTranslate();
674
- });
675
- const afields = test_translator_1.aTableDef.fields
676
- .filter(f => (0, model_1.isAtomic)(f))
677
- .map(f => f.name)
678
- .sort();
679
- test('expands star correctly', () => {
680
- const selstar = (0, test_translator_1.model) `run: ab->{select: *}`;
681
- expect(selstar).toTranslate();
682
- const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
683
- expect(fields).toEqual(afields);
684
- });
685
- test('expands join dot star correctly', () => {
686
- const selstar = (0, test_translator_1.model) `run: ab->{select: b.*}`;
687
- expect(selstar).toTranslate();
688
- const query = selstar.translator.getQuery(0);
689
- expect(query).toBeDefined();
690
- const fields = getFirstSegmentFields(selstar.translator.getQuery(0)).map(f => (f.type === 'fieldref' ? f.path : `wrong field type ${f.type}`));
691
- expect(fields).toEqual(afields.map(f => ['b', f]));
692
- });
693
- test('expands star with exclusions', () => {
694
- const selstar = (0, test_translator_1.model) `run: ab->{select: * { except: ai, except: aun, aweird }}`;
695
- const filterdFields = afields.filter(f => f !== 'ai' && f !== 'aun' && f !== 'aweird');
696
- expect(selstar).toTranslate();
697
- const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
698
- expect(fields).toEqual(filterdFields);
699
- });
700
- test('array in query is passed into fields', () => {
701
- const selArray = (0, test_translator_1.model) `run: a -> { select: ais }`;
702
- expect(selArray).toTranslate();
703
- const fields = getFirstSegmentFieldNames(selArray.translator.getQuery(0));
704
- expect(fields).toEqual(['ais']);
705
- });
706
- test('star error checking', () => {
707
- expect((0, test_translator_1.markSource) `run: a->{select: ${'zzz'}.*}`).toLog((0, test_translator_1.errorMessage)("No such field as 'zzz'"));
708
- expect((0, test_translator_1.markSource) `run: ab->{select: b.${'zzz'}.*}`).toLog((0, test_translator_1.errorMessage)("No such field as 'zzz'"));
709
- expect((0, test_translator_1.markSource) `run: a->{select: ${'ai'}.*}`).toLog((0, test_translator_1.errorMessage)("Field 'ai' does not contain rows and cannot be expanded with '*'"));
710
- expect((0, test_translator_1.markSource) `run: a->{select:ai,${'*'}}`).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in '*' because a field with that name already exists"));
711
- expect((0, test_translator_1.markSource) `run: ab->{select:ai,${'b.*'}}`).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in 'b.*' because a field with that name already exists"));
712
- const m = `
713
- source: nab is a extend {
714
- accept: ai
715
- join_one: b is a extend {accept: ai} on ai=b.ai
716
- }
717
- run: nab->{select: b.*,*}
718
- `;
719
- expect(m).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in '*' because a field with that name already exists (conflicts with b.ai)"));
720
- });
721
- test('regress check extend: and star', () => {
722
- const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
723
- expect(m).toTranslate();
724
- const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
725
- expect(fields).toEqual(afields.concat('x'));
726
- });
727
- test('project def', () => {
728
- expect('run: ab->{ select: one is 1 }').toTranslate();
729
- });
730
- test('project multiple', () => {
731
- expect('run: a->{ select: one is 1, astr }').toTranslate();
732
- });
733
- test('index single', () => { });
734
- test('regress check extend: and star', () => {
735
- const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
736
- expect(m).toTranslate();
737
- const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
738
- expect(fields).toEqual(afields.concat('x'));
739
- });
740
- test('project def', () => {
741
- expect('run:ab->{ select: one is 1 }').toTranslate();
742
- });
743
- test('project multiple', () => {
744
- expect(`
745
- run: a->{
746
- select: one is 1, astr
747
- }
748
- `).toTranslate();
749
- });
750
- test('index single', () => {
751
- expect('run:a->{index: astr}').toTranslate();
752
- });
753
- test('index path', () => {
754
- expect('run:ab->{index: b.astr}').toTranslate();
755
- });
756
- test('index unique on path', () => {
757
- expect('run:ab->{index: astr, b.astr}').toTranslate();
758
- });
759
- test('index join.*', () => {
760
- expect('run:ab->{index: b.*}').toTranslate();
761
- });
762
- test('index multiple', () => {
763
- const model = new test_translator_1.TestTranslator('run:a->{index: af, astr}');
764
- expect(model).toTranslate();
765
- const q = model.getQuery(0);
766
- expect(q).toBeDefined();
767
- if (q) {
768
- const index = q.pipeline[0];
769
- expect(index.type).toBe('index');
770
- if (index.type === 'index') {
771
- expect(index.indexFields).toEqual([
772
- { type: 'fieldref', path: ['af'] },
773
- { type: 'fieldref', path: ['astr'] },
774
- ]);
775
- }
776
- }
777
- });
778
- test('index by', () => {
779
- expect('run:a->{index: * by ai}').toTranslate();
780
- });
781
- test('index sampled', () => {
782
- expect('run:a->{index: *; sample: true}').toTranslate();
783
- });
784
- test('index unsampled', () => {
785
- expect('run:a->{index: *; sample: false}').toTranslate();
786
- });
787
- test('index sample-percent', () => {
788
- const model = new test_translator_1.TestTranslator('run:a->{index: *; sample: 42%}');
789
- expect(model).toTranslate();
790
- const q = model.getQuery(0);
791
- expect(q).toBeDefined();
792
- if (q) {
793
- const index = q.pipeline[0];
794
- expect(index.type).toBe('index');
795
- if (index.type === 'index') {
796
- expect(index.sample).toEqual({ percent: 42 });
797
- }
798
- }
799
- });
800
- test('index sample-rows', () => {
801
- expect('run:a->{index: *; sample: 100000}').toTranslate();
802
- });
803
- test('top N', () => {
804
- expect('run: a->{ top: 5; group_by: astr }').toTranslate();
805
- });
806
- test('limit N', () => {
807
- expect('run: a->{ limit: 5; group_by: astr }').toTranslate();
808
- });
809
- describe('order by variations', () => {
810
- test('order by', () => {
811
- expect('run: a->{ order_by: astr; group_by: astr }').toTranslate();
812
- });
813
- test('order by preserved over refinement', () => {
814
- expect(`
815
- query: a1 is a -> { group_by: astr }
816
- run: a1 + { order_by: astr }
817
- `).toTranslate();
818
- });
819
- test('order by must be in the output space', () => expect('run: a -> { order_by: af; group_by: astr }').toLog((0, test_translator_1.errorMessage)('Unknown field af in output space')));
820
- test('order by asc', () => {
821
- expect('run: a->{ order_by: astr asc; group_by: astr }').toTranslate();
822
- });
823
- test('order by desc', () => {
824
- expect('run: a->{ order_by: astr desc; group_by: astr }').toTranslate();
825
- });
826
- test('order by N', () => {
827
- expect('run: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
828
- });
829
- test('first aggregate used for default ordering', () => {
830
- const m = (0, test_translator_1.model) `run: a->{
831
- group_by: astr
832
- aggregate: t is ai.sum()
833
- }`;
834
- expect(m).toTranslate();
835
- const runStmt = m.translator.getQuery(0);
836
- expect(runStmt).toBeDefined();
837
- const reduce = runStmt.pipeline[0];
838
- expect(reduce.type).toEqual('reduce');
839
- if (reduce.type === 'reduce') {
840
- expect(reduce.defaultOrderBy).toBeTruthy();
841
- expect(reduce.orderBy).toEqual([{ field: 't', dir: 'desc' }]);
842
- }
843
- });
844
- test('first temporal used for default ordering', () => {
845
- const m = (0, test_translator_1.model) `run: a->{
846
- group_by: astr, ats
847
- }`;
848
- expect(m).toTranslate();
849
- const runStmt = m.translator.getQuery(0);
850
- expect(runStmt).toBeDefined();
851
- const reduce = runStmt.pipeline[0];
852
- expect(reduce.type).toEqual('reduce');
853
- if (reduce.type === 'reduce') {
854
- expect(reduce.defaultOrderBy).toBeTruthy();
855
- expect(reduce.orderBy).toEqual([{ field: 'ats', dir: 'desc' }]);
856
- }
857
- });
858
- test('first used for ordering when appropriate', () => {
859
- const m = (0, test_translator_1.model) `run: a->{
860
- group_by: astr, big is upper(astr)
861
- }`;
862
- expect(m).toTranslate();
863
- const runStmt = m.translator.getQuery(0);
864
- expect(runStmt).toBeDefined();
865
- const reduce = runStmt.pipeline[0];
866
- expect(reduce.type).toEqual('reduce');
867
- if (reduce.type === 'reduce') {
868
- expect(reduce.defaultOrderBy).toBeTruthy();
869
- expect(reduce.orderBy).toEqual([{ field: 'astr', dir: 'asc' }]);
870
- }
871
- });
872
- });
873
- test('order by multiple', () => {
874
- expect(`
875
- run: a->{
876
- order_by: 1 asc, af desc
877
- group_by: astr, af
878
- }
879
- `).toTranslate();
880
- });
881
- test('agg cannot be used in where', () => {
882
- expect('run:ab->{ aggregate: acount; group_by: astr; where: acount > 10 }').toLog((0, test_translator_1.errorMessage)('Aggregate expressions are not allowed in `where:`; use `having:`'));
883
- });
884
- test('analytic cannot be used in where', () => {
885
- expect('run:ab->{ calculate: prevc is lag(count()); group_by: astr; where: prevc > 10 }').toLog((0, test_translator_1.errorMessage)("'prevc' is not defined"));
886
- });
887
- test('analytic cannot be used in having', () => {
888
- expect('run:ab->{ calculate: prevc is lag(count()); group_by: astr; having: prevc > 10 }').toLog((0, test_translator_1.errorMessage)('Analytic expressions are not allowed in `having:`'));
889
- });
890
- test('where single', () => {
891
- expect('run:a->{ group_by: astr; where: af > 10 }').toTranslate();
892
- });
893
- test('having single', () => {
894
- expect('run:ab->{ aggregate: acount; group_by: astr; having: acount > 10 }').toTranslate();
895
- });
896
- test('compound having still works', () => {
897
- expect('run:ab->{ aggregate: acount; having: acount > 10 and acount < 100 }').toTranslate();
898
- });
899
- test('compound aggregate still works', () => {
900
- expect('run:ab->{ aggregate: thing is acount > 10 and acount < 100 }').toTranslate();
901
- });
902
- test('where multiple', () => {
903
- expect("run:a->{ group_by: astr; where: af > 10,astr~'a%' }").toTranslate();
904
- });
905
- test('filters preserve source formatting in code:', () => {
906
- const model = new test_translator_1.TestTranslator("source: notb is a extend { where: astr != 'b' }");
907
- expect(model).toTranslate();
908
- const notb = model.getSourceDef('notb');
909
- expect(notb).toBeDefined();
910
- if (notb) {
911
- const f = notb.filterList;
912
- expect(f).toBeDefined();
913
- if (f) {
914
- expect(f[0].code).toBe("astr != 'b'");
915
- }
916
- }
917
- });
918
- test('field expressions preserve source formatting in code:', () => {
919
- const model = new test_translator_1.TestTranslator('source: notb is a extend { dimension: d is 1 + 2 }');
920
- expect(model).toTranslate();
921
- const notb = model.getSourceDef('notb');
922
- expect(notb).toBeDefined();
923
- if (notb) {
924
- const d = notb.fields.find(f => f.as || f.name === 'd');
925
- expect(d).toBeDefined();
926
- expect(d === null || d === void 0 ? void 0 : d.type).toBe('number');
927
- if ((d === null || d === void 0 ? void 0 : d.type) === 'number') {
928
- expect(d.code).toBe('1 + 2');
929
- }
930
- }
931
- });
932
- test('nest single', () => {
933
- expect(`
934
- run: a->{
935
- group_by: ai
936
- nest: nestbystr is { group_by: astr; aggregate: N is count() }
937
- }
938
- `).toTranslate();
939
- });
940
- test('nest multiple', () => expect(`
941
- run: a->{
942
- group_by: ai
943
- nest:
944
- nestbystr is { group_by: astr; aggregate: N is count() },
945
- renest is { group_by: astr; aggregate: N is count() }
946
- }
947
- `).toTranslate());
948
- test('nest ref', () => expect('run: ab->{group_by: ai; nest: aturtle}').toTranslate());
949
- describe('extend block', () => {
950
- test('works with dimension', () => {
951
- expect('run: a -> { extend: { dimension: x is 1 }; group_by: x }').toTranslate();
952
- });
953
- test('works with measure', () => {
954
- expect('run: a -> { extend: { measure: x is count() }; aggregate: x }').toTranslate();
955
- });
956
- test('works with join_one', () => {
957
- expect('run: a -> { extend: { join_one: bb is b on bb.astr = astr }; group_by: bb.astr }').toTranslate();
958
- });
959
- test('works with join_many', () => {
960
- expect('run: a -> { extend: { join_many: b on astr = b.astr }; group_by: b.astr }').toTranslate();
961
- });
962
- test('works with join_cross', () => {
963
- expect('run: a -> { extend: { join_cross: b on true }; group_by: b.astr }').toTranslate();
964
- });
965
- test('works with multiple in one block', () => {
966
- expect('run: a -> { extend: { dimension: x is 1, y is 2 }; group_by: x, y }').toTranslate();
967
- });
968
- test('works with multiple blocks', () => {
969
- expect('run: a -> { extend: { dimension: x is 1; dimension: y is 2; measure: c is count() }; group_by: x, y; aggregate: c }').toTranslate();
970
- });
971
- });
972
- describe('declare/query join warnings', () => {
973
- test('declare warning in query', () => {
974
- expect((0, test_translator_1.markSource) `##! m4warnings=warn
975
- run: a -> { ${'declare: x is 1'}; group_by: x }`).toLog((0, test_translator_1.warningMessage)('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block'));
976
- });
977
- test('declare warning in source', () => {
978
- expect((0, test_translator_1.markSource) `##! m4warnings=warn
979
- source: a2 is a extend { ${'declare: x is 1'} }`).toLog((0, test_translator_1.warningMessage)('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block'));
980
- });
981
- test('joins in query', () => {
982
- expect((0, test_translator_1.markSource) `
983
- run: a -> {
984
- join_one: b on true
985
- join_many: c is b on true
986
- join_cross: d is b on true
987
- group_by: b.astr
988
- }`).toTranslate();
989
- });
990
- });
991
- test('refine query with extended source', () => {
992
- const m = new test_translator_1.TestTranslator(`
993
- run: ab extend {
994
- view: xturtle is aturtle + {
995
- extend: { measure: aratio is ai / acount }
996
- }
997
- } -> xturtle + { aggregate: aratio }
998
- `);
999
- expect(m).toTranslate();
1000
- const t = m.translate();
1001
- if (t.modelDef) {
1002
- const q = t.modelDef.queryList[0].pipeline[0];
1003
- expect(q).toBeDefined();
1004
- if (q.type === 'reduce' && q.extendSource) {
1005
- expect(q.extendSource.length).toBe(1);
1006
- const f = q.extendSource[0];
1007
- expect(f.type).toBe('number');
1008
- if (f.type === 'number') {
1009
- expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
1010
- }
1011
- }
1012
- else {
1013
- fail('Did not generate extendSource');
1014
- }
1015
- }
1016
- });
1017
- test('refine query source with field', () => {
1018
- const m = new test_translator_1.TestTranslator(`
1019
- run: ab -> aturtle + {
1020
- extend: { measure: aratio is ai / acount }
1021
- aggregate: aratio
1022
- }
1023
- `);
1024
- expect(m).toTranslate();
1025
- const t = m.translate();
1026
- if (t.modelDef) {
1027
- const q = t.modelDef.queryList[0].pipeline[0];
1028
- if (q.type === 'reduce' && q.extendSource) {
1029
- expect(q.extendSource.length).toBe(1);
1030
- const f = q.extendSource[0];
1031
- expect(f.type).toBe('number');
1032
- if (f.type === 'number') {
1033
- expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
1034
- }
1035
- }
1036
- else {
1037
- fail('Did not generate extendSource');
1038
- }
1039
- }
1040
- });
1041
- test('refine query source with join', () => {
1042
- const m = new test_translator_1.TestTranslator(`
1043
- run: ab -> aturtle + {
1044
- extend: { join_one: bb is b on bb.astr = astr }
1045
- group_by: foo is bb.astr
1046
- }
1047
- `);
1048
- expect(m).toTranslate();
1049
- const t = m.translate();
1050
- if (t.modelDef) {
1051
- const q = t.modelDef.queryList[0].pipeline[0];
1052
- if (q.type === 'reduce' && q.extendSource) {
1053
- expect(q.extendSource.length).toBe(1);
1054
- expect((0, model_1.isJoined)(q.extendSource[0])).toBeTruthy();
1055
- expect(q.extendSource[0].type).toBe('table');
1056
- }
1057
- else {
1058
- fail('Did not generate extendSource');
1059
- }
1060
- }
1061
- });
1062
- });
1063
- describe('refinement location rules', () => {
1064
- test('where clauses go into the first segment', () => {
1065
- const doc = (0, test_translator_1.model) `
1066
- query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1067
- query: checkme is refineme + { where: astr = 'a' }`;
1068
- expect(doc).toTranslate();
1069
- const checkme = doc.translator.getQuery('checkme');
1070
- expect(checkme).toBeDefined();
1071
- if (checkme) {
1072
- const whereClause = checkme.pipeline[0].filterList;
1073
- expect(whereClause).toBeDefined();
1074
- if (whereClause) {
1075
- expect(whereClause.length).toBe(1);
1076
- }
1077
- }
1078
- });
1079
- test('having clauses go into the last segment', () => {
1080
- const doc = (0, test_translator_1.model) `
1081
- query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
1082
- query: checkme is refineme + { having: ac > 0 }`;
1083
- expect(doc).toTranslate();
1084
- const checkme = doc.translator.getQuery('checkme');
1085
- expect(checkme).toBeDefined();
1086
- if (checkme) {
1087
- const havingClause = checkme.pipeline[1].filterList;
1088
- expect(havingClause).toBeDefined();
1089
- if (havingClause) {
1090
- expect(havingClause.length).toBe(1);
1091
- }
1092
- }
1093
- });
1094
- test('limit goes into the last segment', () => {
1095
- const doc = (0, test_translator_1.model) `
1096
- query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
1097
- query: checkme is refineme + { limit: 1 }`;
1098
- expect(doc).toTranslate();
1099
- const checkme = doc.translator.getQuery('checkme');
1100
- expect(checkme).toBeDefined();
1101
- if (checkme) {
1102
- expect(checkme.pipeline[1]).toMatchObject({ limit: 1 });
1103
- }
1104
- });
1105
- test('order_by goes into the last segment', () => {
1106
- const doc = (0, test_translator_1.model) `
1107
- query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
1108
- query: checkme is refineme + { order_by: 2 }`;
1109
- expect(doc).toTranslate();
1110
- const checkme = doc.translator.getQuery('checkme');
1111
- expect(checkme).toBeDefined();
1112
- if (checkme) {
1113
- expect(checkme.pipeline[1]).toHaveProperty('orderBy');
1114
- }
1115
- });
1116
- const stageErr = 'Illegal in refinement of a query with more than one stage';
1117
- test('group_by illegal in long pipes', () => {
1118
- expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1119
- query: checkme is refineme + { ${'group_by: stage'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
1120
- });
1121
- test('aggregate illegal in long pipes', () => {
1122
- expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1123
- query: checkme is refineme + { ${'aggregate: c is count()'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
1124
- });
1125
- test('calcluate illegal in long pipes', () => {
1126
- expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1127
- query: checkme is refineme + { ${'calculate: c is count()'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
1128
- });
1129
- test('extend illegal in long pipes', () => {
1130
- expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1131
- query: checkme is refineme + { ${'extend: {measure: c is count()}'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
1132
- });
1133
- test('nest illegal in long pipes', () => {
1134
- expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
1135
- query: checkme is refineme + { ${'nest: b is {group_by: stage}'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
1136
- });
1137
- test('all single stage refinements are accepted', () => {
1138
- expect((0, test_translator_1.markSource) `query: refineme is a -> { group_by: stage is "stage1" }
1139
- query: checkme is refineme + {
1140
- extend: { dimension: red is 'red' }
1141
- nest: nestRed is { group_by: red }
1142
- group_by: ai
1143
- aggregate: counted is count()
1144
- calculate: l is lag(ai)
1145
- }`).toTranslate();
1146
- });
1147
- });
1148
- });
1149
- //# sourceMappingURL=query.spec.js.map