@malloydata/malloy 0.0.81-dev230910202545 → 0.0.81
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.
|
@@ -124,7 +124,7 @@ class ExprAggregateFunction extends expression_def_1.ExpressionDef {
|
|
|
124
124
|
const noJoinField = !this.source && joinUsage.every(usage => usage.length === 0);
|
|
125
125
|
if (!noJoinField && !this.isSymmetricFunction()) {
|
|
126
126
|
const usagePaths = getJoinUsagePaths(sourceRelationship, joinUsage);
|
|
127
|
-
const joinError = validateUsagePaths(usagePaths);
|
|
127
|
+
const joinError = validateUsagePaths(this.elementType, usagePaths);
|
|
128
128
|
const message = sourceSpecified
|
|
129
129
|
? joinError
|
|
130
130
|
: 'Join path is required for this calculation';
|
|
@@ -254,14 +254,17 @@ function getJoinUsagePaths(sourceRelationship, joinUsage) {
|
|
|
254
254
|
}
|
|
255
255
|
return sourceToUsagePaths;
|
|
256
256
|
}
|
|
257
|
-
function validateUsagePaths(usagePaths) {
|
|
257
|
+
function validateUsagePaths(functionName, usagePaths) {
|
|
258
258
|
for (const path of usagePaths) {
|
|
259
259
|
for (const step of path) {
|
|
260
260
|
if (step.structRelationship.type === 'cross') {
|
|
261
|
-
return `Cannot compute
|
|
261
|
+
return `Cannot compute \`${functionName}\` across \`join_cross\` relationship \`${step.name}\``;
|
|
262
262
|
}
|
|
263
263
|
else if (step.structRelationship.type === 'many' && !step.reverse) {
|
|
264
|
-
return `Cannot compute
|
|
264
|
+
return `Cannot compute \`${functionName}\` across \`join_many\` relationship \`${step.name}\``;
|
|
265
|
+
}
|
|
266
|
+
else if (step.structRelationship.type === 'nested' && !step.reverse) {
|
|
267
|
+
return `Cannot compute \`${functionName}\` across repeated relationship \`${step.name}\``;
|
|
265
268
|
}
|
|
266
269
|
}
|
|
267
270
|
}
|
|
@@ -283,7 +286,7 @@ function suggestNewVersion(joinError, joinUsage, expr, func) {
|
|
|
283
286
|
}
|
|
284
287
|
}
|
|
285
288
|
const usagePaths = getJoinUsagePaths(longestOverlap, joinUsage);
|
|
286
|
-
const usageError = validateUsagePaths(usagePaths);
|
|
289
|
+
const usageError = validateUsagePaths(func, usagePaths);
|
|
287
290
|
// get rid of everything after the last many/cross
|
|
288
291
|
const indexFromEndOfLastMany = longestOverlap
|
|
289
292
|
.slice()
|
|
@@ -295,7 +298,7 @@ function suggestNewVersion(joinError, joinUsage, expr, func) {
|
|
|
295
298
|
: longestOverlap.length - indexFromEndOfLastMany;
|
|
296
299
|
const shortestOverlapWithMany = longestOverlap.slice(0, numJoinsToLastMany);
|
|
297
300
|
const shortUsagePaths = getJoinUsagePaths(shortestOverlapWithMany, joinUsage);
|
|
298
|
-
const shortUsageError = validateUsagePaths(shortUsagePaths);
|
|
301
|
+
const shortUsageError = validateUsagePaths(func, shortUsagePaths);
|
|
299
302
|
const longLocality = longestOverlap.length > 0
|
|
300
303
|
? longestOverlap.map(r => r.name).join('.')
|
|
301
304
|
: 'source';
|
|
@@ -76,18 +76,22 @@ class Filter extends malloy_element_1.ListOf {
|
|
|
76
76
|
// Aggregates are ALSO checked at SQL generation time, but checking
|
|
77
77
|
// here allows better reflection of errors back to user.
|
|
78
78
|
if (this.havingClause !== undefined) {
|
|
79
|
-
const
|
|
79
|
+
const isAggregate = (0, malloy_types_1.expressionIsAggregate)(fExpr.expressionType);
|
|
80
|
+
const isAnalytic = (0, malloy_types_1.expressionIsAnalytic)(fExpr.expressionType);
|
|
80
81
|
if (this.havingClause) {
|
|
81
|
-
if (
|
|
82
|
-
oneElement.log('
|
|
82
|
+
if (isAnalytic) {
|
|
83
|
+
oneElement.log('Analytic expressions are not allowed in `having:`');
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
else {
|
|
87
|
-
if (
|
|
88
|
-
oneElement.log('
|
|
88
|
+
if (isAnalytic) {
|
|
89
|
+
oneElement.log('Analytic expressions are not allowed in `where:`');
|
|
89
90
|
continue;
|
|
90
91
|
}
|
|
92
|
+
else if (isAggregate) {
|
|
93
|
+
oneElement.log('Aggregate expressions are not allowed in `where:`; use `having:`');
|
|
94
|
+
}
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
checked.push(fExpr);
|
|
@@ -176,6 +176,7 @@ describe('expressions', () => {
|
|
|
176
176
|
source: root is a {
|
|
177
177
|
rename: column is ai
|
|
178
178
|
rename: nested is astruct
|
|
179
|
+
rename: inline is aninline
|
|
179
180
|
dimension: field is column * 2
|
|
180
181
|
dimension: many_field is many.column * 2
|
|
181
182
|
dimension: many_one_field is many.column + one.column
|
|
@@ -277,7 +278,7 @@ describe('expressions', () => {
|
|
|
277
278
|
expect(modelX `sum(many.column)`).translationToFailWith('Join path is required for this calculation; use `many.column.sum()`');
|
|
278
279
|
});
|
|
279
280
|
test('source.sum(many.column)', () => {
|
|
280
|
-
expect(modelX `source.sum(many.column)`).translationToFailWith('Cannot compute
|
|
281
|
+
expect(modelX `source.sum(many.column)`).translationToFailWith('Cannot compute `sum` across `join_many` relationship `many`; use `many.column.sum()`');
|
|
281
282
|
});
|
|
282
283
|
test('many.column.sum()', () => {
|
|
283
284
|
expect(modelX `many.column.sum()`).toTranslate();
|
|
@@ -295,19 +296,28 @@ describe('expressions', () => {
|
|
|
295
296
|
expect(modelX `source.sum(many.constant)`).toTranslate();
|
|
296
297
|
});
|
|
297
298
|
test('sum(nested.column)', () => {
|
|
298
|
-
expect(modelX `sum(nested.column)`).
|
|
299
|
+
expect(modelX `sum(nested.column)`).translationToFailWith('Join path is required for this calculation; use `nested.column.sum()`');
|
|
299
300
|
});
|
|
300
301
|
test('nested.column.sum()', () => {
|
|
301
302
|
expect(modelX `nested.column.sum()`).toTranslate();
|
|
302
303
|
});
|
|
303
304
|
test('source.sum(nested.column)', () => {
|
|
304
|
-
expect(modelX `source.sum(nested.column)`).
|
|
305
|
+
expect(modelX `source.sum(nested.column)`).translationToFailWith('Cannot compute `sum` across repeated relationship `nested`; use `nested.column.sum()`');
|
|
306
|
+
});
|
|
307
|
+
test('sum(inline.column)', () => {
|
|
308
|
+
expect(modelX `sum(inline.column)`).toTranslateWithWarnings('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`');
|
|
309
|
+
});
|
|
310
|
+
test('inline.column.sum()', () => {
|
|
311
|
+
expect(modelX `inline.column.sum()`).toTranslate();
|
|
312
|
+
});
|
|
313
|
+
test('source.sum(inline.column)', () => {
|
|
314
|
+
expect(modelX `source.sum(inline.column)`).toTranslate();
|
|
305
315
|
});
|
|
306
316
|
test('sum(many.field)', () => {
|
|
307
317
|
expect(modelX `sum(many.field)`).translationToFailWith('Join path is required for this calculation; use `many.field.sum()`');
|
|
308
318
|
});
|
|
309
319
|
test('source.sum(many.field)', () => {
|
|
310
|
-
expect(modelX `source.sum(many.field)`).translationToFailWith('Cannot compute
|
|
320
|
+
expect(modelX `source.sum(many.field)`).translationToFailWith('Cannot compute `sum` across `join_many` relationship `many`; use `many.field.sum()`');
|
|
311
321
|
});
|
|
312
322
|
test('many.field.sum()', () => {
|
|
313
323
|
expect(modelX `many.field.sum()`).toTranslate();
|
|
@@ -319,7 +329,7 @@ describe('expressions', () => {
|
|
|
319
329
|
expect(modelX `sum(many.field + many.field)`).translationToFailWith('Join path is required for this calculation; use `many.sum(many.field + many.field)`');
|
|
320
330
|
});
|
|
321
331
|
test('source.sum(many.field + many.field)', () => {
|
|
322
|
-
expect(modelX `source.sum(many.field + many.field)`).translationToFailWith('Cannot compute
|
|
332
|
+
expect(modelX `source.sum(many.field + many.field)`).translationToFailWith('Cannot compute `sum` across `join_many` relationship `many`; use `many.sum(many.field + many.field)`');
|
|
323
333
|
});
|
|
324
334
|
test('many.field + many.field.sum()', () => {
|
|
325
335
|
expect(modelX `many.field + many.field.sum()`).toTranslate();
|
|
@@ -331,7 +341,7 @@ describe('expressions', () => {
|
|
|
331
341
|
expect(modelX `sum(many_field)`).translationToFailWith('Join path is required for this calculation; use `many_field.sum()`');
|
|
332
342
|
});
|
|
333
343
|
test('source.sum(many_field)', () => {
|
|
334
|
-
expect(modelX `source.sum(many_field)`).translationToFailWith('Cannot compute
|
|
344
|
+
expect(modelX `source.sum(many_field)`).translationToFailWith('Cannot compute `sum` across `join_many` relationship `many`; use `many_field.sum()`');
|
|
335
345
|
});
|
|
336
346
|
test('many_field.sum()', () => {
|
|
337
347
|
expect(modelX `many_field.sum()`).toTranslate();
|
|
@@ -343,7 +353,7 @@ describe('expressions', () => {
|
|
|
343
353
|
expect(modelX `sum(one.many_field)`).translationToFailWith('Join path is required for this calculation; use `one.many_field.sum()`');
|
|
344
354
|
});
|
|
345
355
|
test('source.sum(one.many_field)', () => {
|
|
346
|
-
expect(modelX `source.sum(one.many_field)`).translationToFailWith('Cannot compute
|
|
356
|
+
expect(modelX `source.sum(one.many_field)`).translationToFailWith('Cannot compute `sum` across `join_many` relationship `many`; use `one.many_field.sum()`');
|
|
347
357
|
});
|
|
348
358
|
test('one.many_field.sum()', () => {
|
|
349
359
|
expect(modelX `one.many_field.sum()`).toTranslate();
|
|
@@ -382,7 +392,7 @@ describe('expressions', () => {
|
|
|
382
392
|
expect(modelX `one.avg(field)`).toTranslate();
|
|
383
393
|
});
|
|
384
394
|
test('cross.avg(field)', () => {
|
|
385
|
-
expect(modelX `cross.avg(field)`).translationToFailWith('Cannot compute
|
|
395
|
+
expect(modelX `cross.avg(field)`).translationToFailWith('Cannot compute `avg` across `join_cross` relationship `cross`; use `field.avg()`');
|
|
386
396
|
});
|
|
387
397
|
test('cross.avg(cross.field)', () => {
|
|
388
398
|
expect(modelX `cross.avg(cross.field)`).toTranslate();
|
|
@@ -805,6 +805,15 @@ describe('query:', () => {
|
|
|
805
805
|
}
|
|
806
806
|
`).toTranslate();
|
|
807
807
|
});
|
|
808
|
+
test('agg cannot be used in where', () => {
|
|
809
|
+
expect('query:ab->{ aggregate: acount; group_by: astr; where: acount > 10 }').translationToFailWith('Aggregate expressions are not allowed in `where:`; use `having:`');
|
|
810
|
+
});
|
|
811
|
+
test('analytic cannot be used in where', () => {
|
|
812
|
+
expect('query:ab->{ calculate: prevc is lag(count()); group_by: astr; where: prevc > 10 }').translationToFailWith("'prevc' is not defined");
|
|
813
|
+
});
|
|
814
|
+
test('analytic cannot be used in having', () => {
|
|
815
|
+
expect('query:ab->{ calculate: prevc is lag(count()); group_by: astr; having: prevc > 10 }').translationToFailWith('Analytic expressions are not allowed in `having:`');
|
|
816
|
+
});
|
|
808
817
|
test('where single', () => {
|
|
809
818
|
expect('query:a->{ group_by: astr; where: af > 10 }').toTranslate();
|
|
810
819
|
});
|
|
@@ -55,6 +55,14 @@ const mockSchema = {
|
|
|
55
55
|
type: 'struct',
|
|
56
56
|
name: 'astruct',
|
|
57
57
|
structSource: { type: 'nested' },
|
|
58
|
+
structRelationship: { type: 'nested', isArray: true, field: 'foo' },
|
|
59
|
+
fields: [{ type: 'number', name: 'column', numberType: 'integer' }],
|
|
60
|
+
dialect: 'standardsql',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'struct',
|
|
64
|
+
name: 'aninline',
|
|
65
|
+
structSource: { type: 'nested' },
|
|
58
66
|
structRelationship: { type: 'inline' },
|
|
59
67
|
fields: [{ type: 'number', name: 'column', numberType: 'integer' }],
|
|
60
68
|
dialect: 'standardsql',
|