@malloydata/malloy 0.0.190 → 0.0.191-dev240925232958
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lang/ast/expressions/constant-expression.js +4 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.js +25 -14
- package/dist/lang/ast/expressions/expr-alternation-tree.js +1 -3
- package/dist/lang/ast/expressions/expr-cast.js +1 -1
- package/dist/lang/ast/expressions/expr-coalesce.js +1 -1
- package/dist/lang/ast/expressions/expr-func.js +20 -31
- package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
- package/dist/lang/ast/expressions/expr-id-reference.js +1 -3
- package/dist/lang/ast/expressions/expr-props.js +8 -8
- package/dist/lang/ast/expressions/expr-time-extract.js +7 -13
- package/dist/lang/ast/expressions/expr-ungroup.js +4 -8
- package/dist/lang/ast/expressions/for-range.js +2 -3
- package/dist/lang/ast/expressions/function-ordering.js +6 -6
- package/dist/lang/ast/expressions/partial-compare.js +1 -3
- package/dist/lang/ast/expressions/pick-when.js +30 -20
- package/dist/lang/ast/expressions/range.js +1 -1
- package/dist/lang/ast/expressions/top-by.js +1 -1
- package/dist/lang/ast/field-space/dynamic-space.js +1 -1
- package/dist/lang/ast/field-space/index-field-space.js +6 -6
- package/dist/lang/ast/field-space/parameter-space.js +14 -3
- package/dist/lang/ast/field-space/project-field-space.js +1 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +2 -1
- package/dist/lang/ast/field-space/query-spaces.js +7 -6
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/field-space/refined-space.js +3 -3
- package/dist/lang/ast/field-space/static-space.js +11 -2
- package/dist/lang/ast/parameters/has-parameter.js +4 -4
- package/dist/lang/ast/query-builders/index-builder.js +4 -4
- package/dist/lang/ast/query-builders/project-builder.js +2 -2
- package/dist/lang/ast/query-builders/reduce-builder.js +5 -5
- package/dist/lang/ast/query-elements/anonymous-query.js +1 -1
- package/dist/lang/ast/query-elements/query-reference.js +2 -2
- package/dist/lang/ast/query-items/field-declaration.js +7 -4
- package/dist/lang/ast/query-items/field-references.js +1 -1
- package/dist/lang/ast/query-items/typecheck_utils.js +31 -11
- package/dist/lang/ast/query-properties/filters.js +4 -4
- package/dist/lang/ast/query-properties/ordering.js +3 -3
- package/dist/lang/ast/query-properties/qop-desc.js +2 -2
- package/dist/lang/ast/query-properties/top.js +5 -5
- package/dist/lang/ast/query-utils.js +1 -1
- package/dist/lang/ast/source-elements/named-source.d.ts +2 -2
- package/dist/lang/ast/source-elements/named-source.js +12 -13
- package/dist/lang/ast/source-elements/refined-source.js +9 -7
- package/dist/lang/ast/source-elements/sql-source.js +5 -5
- package/dist/lang/ast/source-elements/table-source.js +2 -2
- package/dist/lang/ast/source-properties/joins.js +6 -6
- package/dist/lang/ast/source-properties/renames.js +3 -3
- package/dist/lang/ast/source-query-elements/source-query-element.d.ts +2 -1
- package/dist/lang/ast/source-query-elements/source-query-element.js +3 -2
- package/dist/lang/ast/source-query-elements/sq-arrow.js +2 -2
- package/dist/lang/ast/source-query-elements/sq-extend.js +1 -1
- package/dist/lang/ast/source-query-elements/sq-reference.js +5 -5
- package/dist/lang/ast/source-query-elements/sq-refine.js +2 -2
- package/dist/lang/ast/source-query-elements/sq-source.js +1 -1
- package/dist/lang/ast/sql-elements/sql-string.js +2 -2
- package/dist/lang/ast/statements/define-query.js +2 -2
- package/dist/lang/ast/statements/define-source.js +3 -3
- package/dist/lang/ast/statements/import-statement.js +7 -7
- package/dist/lang/ast/struct-utils.js +1 -1
- package/dist/lang/ast/types/expression-def.js +21 -22
- package/dist/lang/ast/types/lookup-result.d.ts +5 -1
- package/dist/lang/ast/types/malloy-element.d.ts +8 -5
- package/dist/lang/ast/types/malloy-element.js +33 -27
- package/dist/lang/ast/view-elements/qop-desc-view.js +3 -3
- package/dist/lang/ast/view-elements/reference-view.js +6 -6
- package/dist/lang/ast/view-elements/refine-utils.js +6 -6
- package/dist/lang/ast/view-elements/view-arrow.js +1 -1
- package/dist/lang/ast/view-elements/view-refine.js +1 -1
- package/dist/lang/malloy-to-ast.d.ts +7 -7
- package/dist/lang/malloy-to-ast.js +56 -55
- package/dist/lang/parse-log.d.ts +323 -6
- package/dist/lang/parse-log.js +62 -16
- package/dist/lang/parse-malloy.d.ts +5 -3
- package/dist/lang/parse-malloy.js +22 -31
- package/dist/lang/test/annotation.spec.js +4 -4
- package/dist/lang/test/expressions.spec.js +93 -74
- package/dist/lang/test/imports.spec.js +5 -5
- package/dist/lang/test/lenses.spec.js +18 -18
- package/dist/lang/test/literals.spec.js +1 -1
- package/dist/lang/test/locations.spec.js +5 -5
- package/dist/lang/test/parameters.spec.js +34 -34
- package/dist/lang/test/parse-expects.d.ts +10 -28
- package/dist/lang/test/parse-expects.js +38 -33
- package/dist/lang/test/parse.spec.js +49 -49
- package/dist/lang/test/query.spec.js +82 -82
- package/dist/lang/test/source.spec.js +6 -6
- package/dist/lang/test/test-translator.d.ts +19 -0
- package/dist/lang/test/test-translator.js +20 -1
- package/dist/tags.js +4 -2
- package/package.json +1 -1
|
@@ -67,11 +67,11 @@ describe('expressions', () => {
|
|
|
67
67
|
});
|
|
68
68
|
test('typecheck addition lhs', () => {
|
|
69
69
|
const wrong = (0, test_translator_1.expr) `${'"string"'} + 1`;
|
|
70
|
-
expect(wrong).
|
|
70
|
+
expect(wrong).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
|
|
71
71
|
});
|
|
72
72
|
test('typecheck addition rhs', () => {
|
|
73
73
|
const wrong = (0, test_translator_1.expr) `1 + ${'"string"'}`;
|
|
74
|
-
expect(wrong).
|
|
74
|
+
expect(wrong).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
|
|
75
75
|
});
|
|
76
76
|
test('subtraction', () => {
|
|
77
77
|
expect('42 - 7').compilesTo('{42 - 7}');
|
|
@@ -134,16 +134,16 @@ describe('expressions', () => {
|
|
|
134
134
|
expect('ai ?? 7').compilesTo('{ai coalesce 7}');
|
|
135
135
|
});
|
|
136
136
|
test('coalesce type mismatch', () => {
|
|
137
|
-
expect(new test_translator_1.BetaExpression('ai ?? @2003')).
|
|
137
|
+
expect(new test_translator_1.BetaExpression('ai ?? @2003')).toLog((0, test_translator_1.errorMessage)('Mismatched types for coalesce (number, date)'));
|
|
138
138
|
});
|
|
139
139
|
test('disallow date OP number', () => {
|
|
140
|
-
expect(new test_translator_1.BetaExpression('@2001 = 7')).
|
|
140
|
+
expect(new test_translator_1.BetaExpression('@2001 = 7')).toLog((0, test_translator_1.errorMessage)('Cannot compare a date to a number'));
|
|
141
141
|
});
|
|
142
142
|
test('disallow date OP timestamp', () => {
|
|
143
|
-
expect(new test_translator_1.BetaExpression('ad = ats')).
|
|
143
|
+
expect(new test_translator_1.BetaExpression('ad = ats')).toLog((0, test_translator_1.errorMessage)('Cannot compare a date to a timestamp'));
|
|
144
144
|
});
|
|
145
145
|
test('disallow interval from date to timestamp', () => {
|
|
146
|
-
expect(new test_translator_1.BetaExpression('days(ad to ats)')).
|
|
146
|
+
expect(new test_translator_1.BetaExpression('days(ad to ats)')).toLog((0, test_translator_1.errorMessage)('Cannot measure from date to timestamp'));
|
|
147
147
|
});
|
|
148
148
|
test('compare to truncation uses straight comparison', () => {
|
|
149
149
|
expect('ad = ad.quarter').compilesTo('{ad = {timeTrunc-quarter ad}}');
|
|
@@ -165,7 +165,7 @@ describe('expressions', () => {
|
|
|
165
165
|
});
|
|
166
166
|
const noOffset = ['second', 'minute', 'hour'];
|
|
167
167
|
test.each(noOffset.map(x => [x]))('disallow date delta %s', unit => {
|
|
168
|
-
expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).
|
|
168
|
+
expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).toLog((0, test_translator_1.errorMessage)(`Cannot offset date by ${unit}`));
|
|
169
169
|
});
|
|
170
170
|
test('apply with parens', () => {
|
|
171
171
|
expect((0, test_translator_1.expr) `ai ? (> 1 & < 100)`).toTranslate();
|
|
@@ -173,35 +173,35 @@ describe('expressions', () => {
|
|
|
173
173
|
describe('sql friendly warnings', () => {
|
|
174
174
|
test('is null with warning', () => {
|
|
175
175
|
const warnSrc = (0, test_translator_1.expr) `ai is null`;
|
|
176
|
-
expect(warnSrc).
|
|
176
|
+
expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
|
|
177
177
|
expect(warnSrc).compilesTo('{is-null ai}');
|
|
178
178
|
const warning = warnSrc.translator.problems()[0];
|
|
179
179
|
expect(warning.replacement).toEqual('ai = null');
|
|
180
180
|
});
|
|
181
181
|
test('is not null with warning', () => {
|
|
182
182
|
const warnSrc = (0, test_translator_1.expr) `ai is not null`;
|
|
183
|
-
expect(warnSrc).
|
|
183
|
+
expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use '!= NULL' to check for NULL instead of 'IS NOT NULL'"));
|
|
184
184
|
expect(warnSrc).compilesTo('{is-not-null ai}');
|
|
185
185
|
const warning = warnSrc.translator.problems()[0];
|
|
186
186
|
expect(warning.replacement).toEqual('ai != null');
|
|
187
187
|
});
|
|
188
188
|
test('like with warning', () => {
|
|
189
189
|
const warnSrc = (0, test_translator_1.expr) `astr like 'a'`;
|
|
190
|
-
expect(warnSrc).
|
|
190
|
+
expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use Malloy operator '~' instead of 'LIKE'"));
|
|
191
191
|
expect(warnSrc).compilesTo('{astr like "a"}');
|
|
192
192
|
const warning = warnSrc.translator.problems()[0];
|
|
193
193
|
expect(warning.replacement).toEqual("astr ~ 'a'");
|
|
194
194
|
});
|
|
195
195
|
test('NOT LIKE with warning', () => {
|
|
196
196
|
const warnSrc = (0, test_translator_1.expr) `astr not like 'a'`;
|
|
197
|
-
expect(warnSrc).
|
|
197
|
+
expect(warnSrc).toLog((0, test_translator_1.warningMessage)("Use Malloy operator '!~' instead of 'NOT LIKE'"));
|
|
198
198
|
expect(warnSrc).compilesTo('{astr !like "a"}');
|
|
199
199
|
const warning = warnSrc.translator.problems()[0];
|
|
200
200
|
expect(warning.replacement).toEqual("astr !~ 'a'");
|
|
201
201
|
});
|
|
202
202
|
test('is is-null in a model', () => {
|
|
203
203
|
const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is astr is null }`;
|
|
204
|
-
expect(isNullSrc).
|
|
204
|
+
expect(isNullSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
|
|
205
205
|
});
|
|
206
206
|
test('is not-null in a model', () => {
|
|
207
207
|
const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is not null }`;
|
|
@@ -209,7 +209,7 @@ describe('expressions', () => {
|
|
|
209
209
|
});
|
|
210
210
|
test('is not-null is in a model', () => {
|
|
211
211
|
const isNullSrc = (0, test_translator_1.model) `source: xa is a extend { dimension: x1 is not null is null }`;
|
|
212
|
-
expect(isNullSrc).
|
|
212
|
+
expect(isNullSrc).toLog((0, test_translator_1.warningMessage)("Use '= NULL' to check for NULL instead of 'IS NULL'"));
|
|
213
213
|
const warning = isNullSrc.translator.problems()[0];
|
|
214
214
|
expect(warning.replacement).toEqual('null = null');
|
|
215
215
|
});
|
|
@@ -225,7 +225,7 @@ describe('expressions', () => {
|
|
|
225
225
|
});
|
|
226
226
|
test('not null::number', () => {
|
|
227
227
|
const notNull = (0, test_translator_1.expr) `not null::number`;
|
|
228
|
-
expect(notNull).
|
|
228
|
+
expect(notNull).toLog((0, test_translator_1.errorMessage)("'not' Can't use type number"));
|
|
229
229
|
});
|
|
230
230
|
test('(not null)::number', () => {
|
|
231
231
|
const notNull = (0, test_translator_1.expr) `(not null)::number`;
|
|
@@ -246,7 +246,7 @@ describe('expressions', () => {
|
|
|
246
246
|
});
|
|
247
247
|
test('correctly flags filtered scalar', () => {
|
|
248
248
|
const e = new test_translator_1.BetaExpression('ai { where: true }');
|
|
249
|
-
expect(e).
|
|
249
|
+
expect(e).toLog((0, test_translator_1.errorMessage)('Filtered expression requires an aggregate computation'));
|
|
250
250
|
});
|
|
251
251
|
test('correctly flags filtered analytic', () => {
|
|
252
252
|
expect((0, test_translator_1.markSource) `
|
|
@@ -254,7 +254,7 @@ describe('expressions', () => {
|
|
|
254
254
|
group_by: ai
|
|
255
255
|
calculate: l is lag(ai) { where: true }
|
|
256
256
|
}
|
|
257
|
-
`).
|
|
257
|
+
`).toLog((0, test_translator_1.errorMessage)('Filtered expression requires an aggregate computation'));
|
|
258
258
|
});
|
|
259
259
|
describe('expr props', () => {
|
|
260
260
|
test('aggregate order by not allowed without experiments enabled', () => {
|
|
@@ -263,7 +263,7 @@ describe('expressions', () => {
|
|
|
263
263
|
group_by: ai
|
|
264
264
|
aggregate: x1 is string_agg(astr) { order_by: ai }
|
|
265
265
|
}
|
|
266
|
-
`).
|
|
266
|
+
`).toLog((0, test_translator_1.errorMessage)('Enable experiment `aggregate_order_by` to use `order_by` with an aggregate function'));
|
|
267
267
|
});
|
|
268
268
|
test('aggregate limit not allowed without experiments enabled', () => {
|
|
269
269
|
expect((0, test_translator_1.markSource) `
|
|
@@ -271,7 +271,7 @@ describe('expressions', () => {
|
|
|
271
271
|
group_by: ai
|
|
272
272
|
aggregate: x3 is string_agg(astr) { limit: 10 }
|
|
273
273
|
}
|
|
274
|
-
`).
|
|
274
|
+
`).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'aggregate_limit' }));
|
|
275
275
|
});
|
|
276
276
|
test('aggregate order_by not allowed with different experiment enabled', () => {
|
|
277
277
|
expect((0, test_translator_1.markSource) `
|
|
@@ -280,7 +280,7 @@ describe('expressions', () => {
|
|
|
280
280
|
group_by: ai
|
|
281
281
|
aggregate: x1 is string_agg(astr) { order_by: ai }
|
|
282
282
|
}
|
|
283
|
-
`).
|
|
283
|
+
`).toLog((0, test_translator_1.errorMessage)('Enable experiment `aggregate_order_by` to use `order_by` with an aggregate function'));
|
|
284
284
|
});
|
|
285
285
|
test('aggregate limit not allowed with different experiment enabled', () => {
|
|
286
286
|
expect((0, test_translator_1.markSource) `
|
|
@@ -289,18 +289,18 @@ describe('expressions', () => {
|
|
|
289
289
|
group_by: ai
|
|
290
290
|
group_by: x3 is string_agg(astr) { limit: 10 }
|
|
291
291
|
}
|
|
292
|
-
`).
|
|
292
|
+
`).toLog((0, test_translator_1.error)('experiment-not-enabled', { experimentId: 'aggregate_limit' }));
|
|
293
293
|
});
|
|
294
294
|
test('props not allowed on most expressions', () => {
|
|
295
295
|
expect((0, test_translator_1.markSource) `
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
296
|
+
##! experimental { aggregate_order_by aggregate_limit }
|
|
297
|
+
run: a -> {
|
|
298
|
+
group_by: x1 is 1 { order_by: ai }
|
|
299
|
+
group_by: x2 is 1 { partition_by: ai }
|
|
300
|
+
group_by: x3 is 1 { limit: 10 }
|
|
301
|
+
group_by: x4 is 1 { where: ai }
|
|
302
|
+
}
|
|
303
|
+
`).toLog((0, test_translator_1.errorMessage)('`order_by` is not supported for this kind of expression'), (0, test_translator_1.errorMessage)('`partition_by` is not supported for this kind of expression'), (0, test_translator_1.errorMessage)('`limit` is not supported for this kind of expression'), (0, test_translator_1.errorMessage)('Filtered expression requires an aggregate computation'));
|
|
304
304
|
});
|
|
305
305
|
test('analytics can take parititon_by and order_by', () => {
|
|
306
306
|
expect((0, test_translator_1.markSource) `
|
|
@@ -327,7 +327,7 @@ describe('expressions', () => {
|
|
|
327
327
|
calculate: x is lag(ai) { partition_by: ac }
|
|
328
328
|
calculate: y is lag(ai) { partition_by: x }
|
|
329
329
|
}
|
|
330
|
-
`).
|
|
330
|
+
`).toLog((0, test_translator_1.errorMessage)('Partition expression must be scalar or aggregate'), (0, test_translator_1.errorMessage)('Partition expression must be scalar or aggregate'));
|
|
331
331
|
});
|
|
332
332
|
test('analytics order_by requires expression', () => {
|
|
333
333
|
expect((0, test_translator_1.markSource) `
|
|
@@ -336,7 +336,7 @@ describe('expressions', () => {
|
|
|
336
336
|
group_by: ai
|
|
337
337
|
calculate: x is lag(ai) { order_by: asc }
|
|
338
338
|
}
|
|
339
|
-
`).
|
|
339
|
+
`).toLog((0, test_translator_1.errorMessage)('analytic `order_by` must specify an aggregate expression or output field reference'));
|
|
340
340
|
});
|
|
341
341
|
test('string_agg_distinct order by cannot specify expression', () => {
|
|
342
342
|
expect((0, test_translator_1.markSource) `
|
|
@@ -345,7 +345,7 @@ describe('expressions', () => {
|
|
|
345
345
|
group_by: ai
|
|
346
346
|
aggregate: x is string_agg_distinct(astr) { order_by: ai }
|
|
347
347
|
}
|
|
348
|
-
`).
|
|
348
|
+
`).toLog((0, test_translator_1.errorMessage)('`order_by` must be only `asc` or `desc` with no expression'));
|
|
349
349
|
});
|
|
350
350
|
test('string_agg_distinct order by can be just direction', () => {
|
|
351
351
|
expect((0, test_translator_1.markSource) `
|
|
@@ -396,7 +396,7 @@ describe('expressions', () => {
|
|
|
396
396
|
order_by: sum(ai)
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
|
-
`).
|
|
399
|
+
`).toLog((0, test_translator_1.errorMessage)('aggregate `order_by` must be scalar'));
|
|
400
400
|
});
|
|
401
401
|
test('aggregate order by cannot be analytic', () => {
|
|
402
402
|
expect((0, test_translator_1.markSource) `
|
|
@@ -406,7 +406,7 @@ describe('expressions', () => {
|
|
|
406
406
|
order_by: rank()
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
|
-
`).
|
|
409
|
+
`).toLog((0, test_translator_1.errorMessage)('aggregate `order_by` must be scalar'));
|
|
410
410
|
});
|
|
411
411
|
test('analytic order by can be an aggregate', () => {
|
|
412
412
|
expect((0, test_translator_1.markSource) `
|
|
@@ -439,7 +439,7 @@ describe('expressions', () => {
|
|
|
439
439
|
order_by: ai
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
|
-
`).
|
|
442
|
+
`).toLog((0, test_translator_1.errorMessage)('analytic `order_by` must be an aggregate or an output field reference'));
|
|
443
443
|
});
|
|
444
444
|
test('can specify multiple wheres', () => {
|
|
445
445
|
expect((0, test_translator_1.markSource) `
|
|
@@ -472,6 +472,7 @@ describe('expressions', () => {
|
|
|
472
472
|
rename: nested is astruct
|
|
473
473
|
rename: inline is aninline
|
|
474
474
|
dimension: field is column * 2
|
|
475
|
+
dimension: field_and_one_field is column + one.column
|
|
475
476
|
dimension: many_field is many.column * 2
|
|
476
477
|
dimension: many_one_field is many.column + one.column
|
|
477
478
|
join_one: one is a extend {
|
|
@@ -513,7 +514,7 @@ describe('expressions', () => {
|
|
|
513
514
|
expect(modelX `one.column.min()`).toTranslate();
|
|
514
515
|
});
|
|
515
516
|
test('one.min(one.column)', () => {
|
|
516
|
-
expect(modelX `one.min(one.column)`).
|
|
517
|
+
expect(modelX `one.min(one.column)`).toLog((0, test_translator_1.errorMessage)('Symmetric aggregate function `min` must be written as `min(expression)` or `path.to.field.min()`'));
|
|
517
518
|
});
|
|
518
519
|
test('min(one.column)', () => {
|
|
519
520
|
expect(modelX `min(one.column)`).toTranslate();
|
|
@@ -522,7 +523,7 @@ describe('expressions', () => {
|
|
|
522
523
|
expect(modelX `min(many.column)`).toTranslate();
|
|
523
524
|
});
|
|
524
525
|
test('min()', () => {
|
|
525
|
-
expect(modelX `min()`).
|
|
526
|
+
expect(modelX `min()`).toLog((0, test_translator_1.errorMessage)('Symmetric aggregate function `min` must be written as `min(expression)` or `path.to.field.min()`'));
|
|
526
527
|
});
|
|
527
528
|
test('source.min(column)', () => {
|
|
528
529
|
expect(modelX `source.min(column)`).toTranslate();
|
|
@@ -534,7 +535,7 @@ describe('expressions', () => {
|
|
|
534
535
|
expect(modelX `max(many.column)`).toTranslate();
|
|
535
536
|
});
|
|
536
537
|
test('max()', () => {
|
|
537
|
-
expect(modelX `max()`).
|
|
538
|
+
expect(modelX `max()`).toLog((0, test_translator_1.errorMessage)('Symmetric aggregate function `max` must be written as `max(expression)` or `path.to.field.max()`'));
|
|
538
539
|
});
|
|
539
540
|
test('source.max(many.column)', () => {
|
|
540
541
|
expect(modelX `source.max(many.column)`).toTranslate();
|
|
@@ -555,7 +556,7 @@ describe('expressions', () => {
|
|
|
555
556
|
expect(modelX `many.count()`).toTranslate();
|
|
556
557
|
});
|
|
557
558
|
test('sum()', () => {
|
|
558
|
-
expect(modelX `sum()`).
|
|
559
|
+
expect(modelX `sum()`).toLog((0, test_translator_1.errorMessage)('Asymmetric aggregate function `sum` must be written as `path.to.field.sum()`, `path.to.join.sum(expression)`, or `sum(expression)`'));
|
|
559
560
|
});
|
|
560
561
|
test('sum(column)', () => {
|
|
561
562
|
expect(modelX `sum(column)`).toTranslate();
|
|
@@ -570,10 +571,10 @@ describe('expressions', () => {
|
|
|
570
571
|
expect(modelX `source.sum(column)`).toTranslate();
|
|
571
572
|
});
|
|
572
573
|
test('sum(many.column)', () => {
|
|
573
|
-
expect(modelX `sum(many.column)`).
|
|
574
|
+
expect(modelX `sum(many.column)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many.column.sum()`'));
|
|
574
575
|
});
|
|
575
576
|
test('source.sum(many.column)', () => {
|
|
576
|
-
expect(modelX `source.sum(many.column)`).
|
|
577
|
+
expect(modelX `source.sum(many.column)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across `join_many` relationship `many`; use `many.column.sum()`'));
|
|
577
578
|
});
|
|
578
579
|
test('many.column.sum()', () => {
|
|
579
580
|
expect(modelX `many.column.sum()`).toTranslate();
|
|
@@ -582,7 +583,7 @@ describe('expressions', () => {
|
|
|
582
583
|
expect(modelX `many.sum(many.column)`).toTranslate();
|
|
583
584
|
});
|
|
584
585
|
test('sum(one.column)', () => {
|
|
585
|
-
expect(modelX `sum(one.column)`).
|
|
586
|
+
expect(modelX `sum(one.column)`).toLog((0, test_translator_1.warningMessage)('Join path is required for this calculation; use `one.column.sum()` or `source.sum(one.column)` to get a result weighted with respect to `source`'));
|
|
586
587
|
});
|
|
587
588
|
test('sum(many.constant)', () => {
|
|
588
589
|
expect(modelX `sum(many.constant)`).toTranslate();
|
|
@@ -591,13 +592,13 @@ describe('expressions', () => {
|
|
|
591
592
|
expect(modelX `source.sum(many.constant)`).toTranslate();
|
|
592
593
|
});
|
|
593
594
|
test('sum(nested.column)', () => {
|
|
594
|
-
expect(modelX `sum(nested.column)`).
|
|
595
|
+
expect(modelX `sum(nested.column)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `nested.column.sum()`'));
|
|
595
596
|
});
|
|
596
597
|
test('nested.column.sum()', () => {
|
|
597
598
|
expect(modelX `nested.column.sum()`).toTranslate();
|
|
598
599
|
});
|
|
599
600
|
test('source.sum(nested.column)', () => {
|
|
600
|
-
expect(modelX `source.sum(nested.column)`).
|
|
601
|
+
expect(modelX `source.sum(nested.column)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across repeated relationship `nested`; use `nested.column.sum()`'));
|
|
601
602
|
});
|
|
602
603
|
test('can aggregate field defined with no join usage', () => {
|
|
603
604
|
expect((0, test_translator_1.markSource) `
|
|
@@ -612,7 +613,7 @@ describe('expressions', () => {
|
|
|
612
613
|
`).toTranslate();
|
|
613
614
|
});
|
|
614
615
|
test('sum(inline.column)', () => {
|
|
615
|
-
expect(modelX `sum(inline.column)`).
|
|
616
|
+
expect(modelX `sum(inline.column)`).toLog((0, test_translator_1.warningMessage)('Join path is required for this calculation; use `inline.column.sum()` or `source.sum(inline.column)` to get a result weighted with respect to `source`'));
|
|
616
617
|
});
|
|
617
618
|
test('inline.column.sum()', () => {
|
|
618
619
|
expect(modelX `inline.column.sum()`).toTranslate();
|
|
@@ -621,10 +622,10 @@ describe('expressions', () => {
|
|
|
621
622
|
expect(modelX `source.sum(inline.column)`).toTranslate();
|
|
622
623
|
});
|
|
623
624
|
test('sum(many.field)', () => {
|
|
624
|
-
expect(modelX `sum(many.field)`).
|
|
625
|
+
expect(modelX `sum(many.field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many.field.sum()`'));
|
|
625
626
|
});
|
|
626
627
|
test('source.sum(many.field)', () => {
|
|
627
|
-
expect(modelX `source.sum(many.field)`).
|
|
628
|
+
expect(modelX `source.sum(many.field)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across `join_many` relationship `many`; use `many.field.sum()`'));
|
|
628
629
|
});
|
|
629
630
|
test('many.field.sum()', () => {
|
|
630
631
|
expect(modelX `many.field.sum()`).toTranslate();
|
|
@@ -633,10 +634,10 @@ describe('expressions', () => {
|
|
|
633
634
|
expect(modelX `many.sum(many.field)`).toTranslate();
|
|
634
635
|
});
|
|
635
636
|
test('sum(many.field + many.field)', () => {
|
|
636
|
-
expect(modelX `sum(many.field + many.field)`).
|
|
637
|
+
expect(modelX `sum(many.field + many.field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many.sum(many.field + many.field)`'));
|
|
637
638
|
});
|
|
638
639
|
test('source.sum(many.field + many.field)', () => {
|
|
639
|
-
expect(modelX `source.sum(many.field + many.field)`).
|
|
640
|
+
expect(modelX `source.sum(many.field + many.field)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across `join_many` relationship `many`; use `many.sum(many.field + many.field)`'));
|
|
640
641
|
});
|
|
641
642
|
test('many.field + many.field.sum()', () => {
|
|
642
643
|
expect(modelX `many.field + many.field.sum()`).toTranslate();
|
|
@@ -645,10 +646,10 @@ describe('expressions', () => {
|
|
|
645
646
|
expect(modelX `many.sum(many.field + many.field)`).toTranslate();
|
|
646
647
|
});
|
|
647
648
|
test('sum(many_field)', () => {
|
|
648
|
-
expect(modelX `sum(many_field)`).
|
|
649
|
+
expect(modelX `sum(many_field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many_field.sum()`'));
|
|
649
650
|
});
|
|
650
651
|
test('source.sum(many_field)', () => {
|
|
651
|
-
expect(modelX `source.sum(many_field)`).
|
|
652
|
+
expect(modelX `source.sum(many_field)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across `join_many` relationship `many`; use `many_field.sum()`'));
|
|
652
653
|
});
|
|
653
654
|
test('many_field.sum()', () => {
|
|
654
655
|
expect(modelX `many_field.sum()`).toTranslate();
|
|
@@ -657,10 +658,10 @@ describe('expressions', () => {
|
|
|
657
658
|
expect(modelX `many.sum(many_field)`).toTranslate();
|
|
658
659
|
});
|
|
659
660
|
test('sum(one.many_field)', () => {
|
|
660
|
-
expect(modelX `sum(one.many_field)`).
|
|
661
|
+
expect(modelX `sum(one.many_field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `one.many_field.sum()`'));
|
|
661
662
|
});
|
|
662
663
|
test('source.sum(one.many_field)', () => {
|
|
663
|
-
expect(modelX `source.sum(one.many_field)`).
|
|
664
|
+
expect(modelX `source.sum(one.many_field)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `sum` across `join_many` relationship `many`; use `one.many_field.sum()`'));
|
|
664
665
|
});
|
|
665
666
|
test('one.many_field.sum()', () => {
|
|
666
667
|
expect(modelX `one.many_field.sum()`).toTranslate();
|
|
@@ -669,28 +670,31 @@ describe('expressions', () => {
|
|
|
669
670
|
expect(modelX `one.many.sum(one.many_field)`).toTranslate();
|
|
670
671
|
});
|
|
671
672
|
test('sum(many.field + one.field)', () => {
|
|
672
|
-
expect(modelX `sum(many.field + one.field)`).
|
|
673
|
+
expect(modelX `sum(many.field + one.field)`).toLog((0, test_translator_1.errorMessage)('Aggregated dimensional expression contains multiple join paths; rewrite, for example `sum(first_join.field + second_join.field)` as `first_join.field.sum() + second_join.field.sum()`'));
|
|
673
674
|
});
|
|
674
675
|
test('source.sum(many.field + one.field)', () => {
|
|
675
|
-
expect(modelX `source.sum(many.field + one.field)`).
|
|
676
|
+
expect(modelX `source.sum(many.field + one.field)`).toLog((0, test_translator_1.errorMessage)('Aggregated dimensional expression contains multiple join paths; rewrite, for example `sum(first_join.field + second_join.field)` as `first_join.field.sum() + second_join.field.sum()`'));
|
|
676
677
|
});
|
|
677
678
|
test('many.sum(many.field + one.field)', () => {
|
|
678
679
|
expect(modelX `many.sum(many.field + one.field)`).toTranslate();
|
|
679
680
|
});
|
|
681
|
+
test('many_one_field.sum()', () => {
|
|
682
|
+
expect(modelX `many_one_field.sum()`).toLog((0, test_translator_1.errorMessage)('Aggregated dimensional expression contains multiple join paths; rewrite, for example `sum(first_join.field + second_join.field)` as `first_join.field.sum() + second_join.field.sum()`'));
|
|
683
|
+
});
|
|
680
684
|
test('sum(many_one_field)', () => {
|
|
681
|
-
expect(modelX `sum(many_one_field)`).
|
|
685
|
+
expect(modelX `sum(many_one_field)`).toLog((0, test_translator_1.errorMessage)('Aggregated dimensional expression contains multiple join paths; rewrite, for example `sum(first_join.field + second_join.field)` as `first_join.field.sum() + second_join.field.sum()`'));
|
|
682
686
|
});
|
|
683
687
|
test('source.sum(many_one_field)', () => {
|
|
684
|
-
expect(modelX `source.sum(many_one_field)`).
|
|
688
|
+
expect(modelX `source.sum(many_one_field)`).toLog((0, test_translator_1.errorMessage)('Aggregated dimensional expression contains multiple join paths; rewrite, for example `sum(first_join.field + second_join.field)` as `first_join.field.sum() + second_join.field.sum()`'));
|
|
685
689
|
});
|
|
686
690
|
test('many.sum(many_one_field)', () => {
|
|
687
691
|
expect(modelX `many.sum(many_one_field)`).toTranslate();
|
|
688
692
|
});
|
|
689
693
|
test('sum(many.one.field)', () => {
|
|
690
|
-
expect(modelX `sum(many.one.field)`).
|
|
694
|
+
expect(modelX `sum(many.one.field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many.one.field.sum()` or `many.sum(many.one.field)` to get a result weighted with respect to `many`'));
|
|
691
695
|
});
|
|
692
696
|
test('sum(many.one.one.field)', () => {
|
|
693
|
-
expect(modelX `sum(many.one.one.field)`).
|
|
697
|
+
expect(modelX `sum(many.one.one.field)`).toLog((0, test_translator_1.errorMessage)('Join path is required for this calculation; use `many.one.one.field.sum()` or `many.sum(many.one.one.field)` to get a result weighted with respect to `many`'));
|
|
694
698
|
});
|
|
695
699
|
test('many.avg(field)', () => {
|
|
696
700
|
expect(modelX `many.avg(field)`).toTranslate();
|
|
@@ -699,7 +703,7 @@ describe('expressions', () => {
|
|
|
699
703
|
expect(modelX `one.avg(field)`).toTranslate();
|
|
700
704
|
});
|
|
701
705
|
test('cross.avg(field)', () => {
|
|
702
|
-
expect(modelX `cross.avg(field)`).
|
|
706
|
+
expect(modelX `cross.avg(field)`).toLog((0, test_translator_1.errorMessage)('Cannot compute `avg` across `join_cross` relationship `cross`; use `field.avg()`'));
|
|
703
707
|
});
|
|
704
708
|
test('cross.avg(cross.field)', () => {
|
|
705
709
|
expect(modelX `cross.avg(cross.field)`).toTranslate();
|
|
@@ -714,7 +718,7 @@ describe('expressions', () => {
|
|
|
714
718
|
expect(modelX `source.sum(one.column)`).toTranslate();
|
|
715
719
|
});
|
|
716
720
|
test('sum(one.column + one.column)', () => {
|
|
717
|
-
expect(modelX `sum(one.column + one.column)`).
|
|
721
|
+
expect(modelX `sum(one.column + one.column)`).toLog((0, test_translator_1.warningMessage)('Join path is required for this calculation; use `one.sum(one.column + one.column)` or `source.sum(one.column + one.column)` to get a result weighted with respect to `source`'));
|
|
718
722
|
});
|
|
719
723
|
test('one.sum(one.column + one.column)', () => {
|
|
720
724
|
expect(modelX `one.sum(one.column + one.column)`).toTranslate();
|
|
@@ -728,7 +732,7 @@ describe('expressions', () => {
|
|
|
728
732
|
run: a -> {
|
|
729
733
|
group_by: output is 1
|
|
730
734
|
calculate: bar is lag(sum(output))
|
|
731
|
-
}`).
|
|
735
|
+
}`).toLog((0, test_translator_1.errorMessage)("'output' is not defined"));
|
|
732
736
|
});
|
|
733
737
|
});
|
|
734
738
|
describe('pick statements', () => {
|
|
@@ -784,11 +788,11 @@ describe('expressions', () => {
|
|
|
784
788
|
`).toTranslate();
|
|
785
789
|
});
|
|
786
790
|
test('n-ary without else', () => {
|
|
787
|
-
expect(`
|
|
791
|
+
return expect(`
|
|
788
792
|
source: na is a extend { dimension: d is
|
|
789
793
|
pick 7 when true and true
|
|
790
794
|
}
|
|
791
|
-
`).
|
|
795
|
+
`).toLog((0, test_translator_1.error)('pick-missing-else'));
|
|
792
796
|
});
|
|
793
797
|
test('n-ary with mismatch when clauses', () => {
|
|
794
798
|
expect((0, test_translator_1.markSource) `
|
|
@@ -797,7 +801,10 @@ describe('expressions', () => {
|
|
|
797
801
|
pick '7' when true or true
|
|
798
802
|
else 7
|
|
799
803
|
}
|
|
800
|
-
`).
|
|
804
|
+
`).toLog((0, test_translator_1.error)('pick-type-does-not-match', {
|
|
805
|
+
pickType: 'string',
|
|
806
|
+
returnType: 'number',
|
|
807
|
+
}));
|
|
801
808
|
});
|
|
802
809
|
test('n-ary with mismatched else clause', () => {
|
|
803
810
|
expect((0, test_translator_1.markSource) `
|
|
@@ -805,28 +812,40 @@ describe('expressions', () => {
|
|
|
805
812
|
pick 7 when true and true
|
|
806
813
|
else '7'
|
|
807
814
|
}
|
|
808
|
-
`).
|
|
815
|
+
`).toLog((0, test_translator_1.error)('pick-else-type-does-not-match', {
|
|
816
|
+
elseType: 'string',
|
|
817
|
+
returnType: 'number',
|
|
818
|
+
}));
|
|
809
819
|
});
|
|
810
820
|
test('applied else mismatch', () => {
|
|
811
821
|
expect((0, test_translator_1.markSource) `
|
|
812
822
|
source: na is a extend { dimension: d is
|
|
813
823
|
7 ? pick 7 when 7 else 'not seven'
|
|
814
824
|
}
|
|
815
|
-
`).
|
|
825
|
+
`).toLog((0, test_translator_1.error)('pick-else-type-does-not-match', {
|
|
826
|
+
elseType: 'string',
|
|
827
|
+
returnType: 'number',
|
|
828
|
+
}));
|
|
816
829
|
});
|
|
817
830
|
test('applied default mismatch', () => {
|
|
818
831
|
expect((0, test_translator_1.markSource) `
|
|
819
832
|
source: na is a extend { dimension: d is
|
|
820
833
|
7 ? pick 'seven' when 7
|
|
821
834
|
}
|
|
822
|
-
`).
|
|
835
|
+
`).toLog((0, test_translator_1.error)('pick-default-type-does-not-match', {
|
|
836
|
+
defaultType: 'number',
|
|
837
|
+
returnType: 'string',
|
|
838
|
+
}));
|
|
823
839
|
});
|
|
824
840
|
test('applied when mismatch', () => {
|
|
825
841
|
expect((0, test_translator_1.markSource) `
|
|
826
842
|
source: na is a extend { dimension: d is
|
|
827
843
|
7 ? pick 'seven' when 7 pick 6 when 6
|
|
828
844
|
}
|
|
829
|
-
`).
|
|
845
|
+
`).toLog((0, test_translator_1.error)('pick-type-does-not-match', {
|
|
846
|
+
pickType: 'number',
|
|
847
|
+
returnType: 'string',
|
|
848
|
+
}));
|
|
830
849
|
});
|
|
831
850
|
});
|
|
832
851
|
test('paren and applied div', () => {
|
|
@@ -854,12 +873,12 @@ describe('sql native fields in schema', () => {
|
|
|
854
873
|
test('flag unsupported equality', () => {
|
|
855
874
|
// because we don't know if the two unsupported types are comparable
|
|
856
875
|
const uModel = new test_translator_1.TestTranslator('run: ab->{ where: aun = b.aun select: * }');
|
|
857
|
-
expect(uModel).
|
|
876
|
+
expect(uModel).toLog((0, test_translator_1.error)('sql-native-not-allowed-in-expression', { rawType: undefined }));
|
|
858
877
|
});
|
|
859
878
|
test('flag unsupported compare', () => {
|
|
860
879
|
// because we don't know if the two unsupported types are comparable
|
|
861
880
|
const uModel = new test_translator_1.TestTranslator('run: ab->{ where: aun > b.aun select: * }');
|
|
862
|
-
expect(uModel).
|
|
881
|
+
expect(uModel).toLog((0, test_translator_1.error)('sql-native-not-allowed-in-expression', { rawType: undefined }));
|
|
863
882
|
});
|
|
864
883
|
test('allow unsupported equality when raw types match', () => {
|
|
865
884
|
const uModel = new test_translator_1.TestTranslator('run: ab->{ where: aweird = b.aweird select: * }');
|
|
@@ -867,7 +886,7 @@ describe('sql native fields in schema', () => {
|
|
|
867
886
|
});
|
|
868
887
|
test('flag not applied to unsupported', () => {
|
|
869
888
|
const uModel = new test_translator_1.TestTranslator('source: x is a extend { dimension: notUn is not aun }');
|
|
870
|
-
expect(uModel).
|
|
889
|
+
expect(uModel).toLog((0, test_translator_1.error)('sql-native-not-allowed-in-expression', { rawType: undefined }));
|
|
871
890
|
});
|
|
872
891
|
test('allow unsupported to be cast', () => {
|
|
873
892
|
const uModel = new test_translator_1.TestTranslator('source: x is a extend { dimension: notUn is aun::string }');
|
|
@@ -891,14 +910,14 @@ describe('sql native fields in schema', () => {
|
|
|
891
910
|
run: a -> {
|
|
892
911
|
group_by: x is sql_number("\${asdfasdf} * 2")
|
|
893
912
|
}
|
|
894
|
-
`).
|
|
913
|
+
`).toLog((0, test_translator_1.errorMessage)("Invalid interpolation: 'asdfasdf' is not defined"));
|
|
895
914
|
});
|
|
896
915
|
test('error when using sql_ function without experiment', () => {
|
|
897
916
|
expect(`
|
|
898
917
|
run: a -> {
|
|
899
918
|
group_by: x is sql_number("\${asdfasdf} * 2")
|
|
900
919
|
}
|
|
901
|
-
`).
|
|
920
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot use sql_function `sql_number`; use `sql_functions` experiment to enable this behavior'));
|
|
902
921
|
});
|
|
903
922
|
});
|
|
904
923
|
describe('cast', () => {
|
|
@@ -920,7 +939,7 @@ describe('sql native fields in schema', () => {
|
|
|
920
939
|
expect((0, test_translator_1.expr) `astr:::number + 1`).toTranslate();
|
|
921
940
|
});
|
|
922
941
|
test('sql cast illegal type name', () => {
|
|
923
|
-
expect((0, test_translator_1.expr) `astr::"stuff 'n' things"`).
|
|
942
|
+
expect((0, test_translator_1.expr) `astr::"stuff 'n' things"`).toLog((0, test_translator_1.errorMessage)("Cast type `stuff 'n' things` is invalid for standardsql dialect"));
|
|
924
943
|
});
|
|
925
944
|
});
|
|
926
945
|
});
|
|
@@ -110,7 +110,7 @@ source: botProjQSrc is botProjQ
|
|
|
110
110
|
},
|
|
111
111
|
});
|
|
112
112
|
docParse.translate();
|
|
113
|
-
expect(docParse).
|
|
113
|
+
expect(docParse).toLog((0, test_translator_1.errorMessage)(new RegExp((0, escapeRegExp_1.default)(reportedError))));
|
|
114
114
|
});
|
|
115
115
|
test('chained imports', () => {
|
|
116
116
|
const docParse = new test_translator_1.TestTranslator('import "child"');
|
|
@@ -156,7 +156,7 @@ source: botProjQSrc is botProjQ
|
|
|
156
156
|
}`,
|
|
157
157
|
},
|
|
158
158
|
});
|
|
159
|
-
expect(docParse).
|
|
159
|
+
expect(docParse).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'astr'"));
|
|
160
160
|
});
|
|
161
161
|
test('source references expanded when not exported', () => {
|
|
162
162
|
const srcFiles = {
|
|
@@ -221,7 +221,7 @@ source: botProjQSrc is botProjQ
|
|
|
221
221
|
'internal://test/langtests/child': 'source: aa is a',
|
|
222
222
|
},
|
|
223
223
|
});
|
|
224
|
-
expect(doc.translator).
|
|
224
|
+
expect(doc.translator).toLog((0, test_translator_1.errorMessage)("Cannot find 'bb', not imported"));
|
|
225
225
|
});
|
|
226
226
|
test('selective renamed import of source, not found', () => {
|
|
227
227
|
const doc = (0, test_translator_1.model) `import { cc is ${'bb'} } from "child"`;
|
|
@@ -233,7 +233,7 @@ source: botProjQSrc is botProjQ
|
|
|
233
233
|
'internal://test/langtests/child': 'source: aa is a',
|
|
234
234
|
},
|
|
235
235
|
});
|
|
236
|
-
expect(doc.translator).
|
|
236
|
+
expect(doc.translator).toLog((0, test_translator_1.errorMessage)("Cannot find 'bb', not imported"));
|
|
237
237
|
});
|
|
238
238
|
test('selective import of source, no-redefinition', () => {
|
|
239
239
|
const doc = (0, test_translator_1.model) `
|
|
@@ -247,7 +247,7 @@ source: botProjQSrc is botProjQ
|
|
|
247
247
|
'internal://test/langtests/child': 'source: bb is a',
|
|
248
248
|
},
|
|
249
249
|
});
|
|
250
|
-
expect(doc.translator).
|
|
250
|
+
expect(doc.translator).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'cc'"));
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
//# sourceMappingURL=imports.spec.js.map
|